updated plugin Menu Icons version 0.13.17

This commit is contained in:
2025-04-29 21:19:59 +00:00
committed by Gitium
parent ebd40ef928
commit fd76ba0cbe
38 changed files with 1332 additions and 243 deletions

View File

@ -39,6 +39,8 @@ abstract class Abstract_Module {
'multiple-pages-generator-by-porthas' => 'multiple-pages-generator-by-porthas/porthas-multi-pages-generator.php',
'sparks-for-woocommerce' => 'sparks-for-woocommerce/sparks-for-woocommerce.php',
'templates-patterns-collection' => 'templates-patterns-collection/templates-patterns-collection.php',
'wpcf7-redirect' => 'wpcf7-redirect/wpcf7-redirect.php',
'wp-full-stripe-free' => 'wp-full-stripe-free/wp-full-stripe.php',
];
/**
@ -111,7 +113,7 @@ abstract class Abstract_Module {
* hence we also need to check that the path does not point to the theme else this will break the URL.
* References: https://github.com/Codeinwp/neve-pro-addon/issues/2403
*/
if ( $this->product->is_plugin() && false === strpos( $themeisle_sdk_max_path, get_template_directory() ) ) {
if ( ( $this->product->is_plugin() || $this->product->is_theme() ) && false === strpos( $themeisle_sdk_max_path, get_template_directory() ) ) {
return plugins_url( '/', $themeisle_sdk_max_path . '/themeisle-sdk/' );
};

View File

@ -53,6 +53,7 @@ final class Loader {
'licenser',
'logger',
'translate',
'translations',
'review',
'recommendation',
'notification',
@ -113,8 +114,16 @@ final class Loader {
'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.',
],
'promotions' => [
'recommended' => 'Recommended by %s',
'woo' => [
'recommended' => 'Recommended by %s',
'installActivate' => 'Install & Activate',
'preview' => 'Preview',
'installing' => 'Installing',
'activating' => 'Activating',
'connecting' => 'Connecting to API',
'learnmore' => 'Learn More',
'activate' => 'Activate',
'all_set' => 'Awesome! You are all set!',
'woo' => [
'title' => 'More extensions from Themeisle',
'title2' => 'Recommended extensions',
'cta_install' => 'Install',
@ -129,22 +138,39 @@ final class Loader {
'spark_desc2' => 'Add a top notification bar on your website to highlight the latest products, offers, or upcoming events.',
'spark_desc3' => 'Enable an advanced review section, enlarging the basic review options with lots of capabilities.',
],
'optimole' => [
'all_set' => 'Awesome! You are all set!',
'gotodash' => 'Go to Optimole dashboard',
'installing' => 'Installing',
'activating' => 'Activating',
'connecting' => 'Connecting to API',
'start_cta' => 'Start using Optimole',
'dismisscta' => 'Dismiss this notice.',
'gst' => 'Get Started Free',
'heading' => 'Get more with Optimole',
'learnmore' => 'Learn more',
'message1' => 'Increase this page speed and SEO ranking by optimizing images with Optimole.',
'message3' => 'Save your server space by storing images to Optimole and deliver them optimized from 400 locations around the globe. Unlimited images, Unlimited traffic.',
'message4' => 'This image looks to be too large and would affect your site speed, we recommend you to install Optimole to optimize your images.',
'message2' => 'Leverage Optimole\'s full integration with Elementor to automatically lazyload, resize, compress to AVIF/WebP and deliver from 400 locations around the globe!',
'email_placeholder' => 'Email address',
'feedzy' => [
'import_desc' => 'Schedule automatic content imports from any RSS feed directly to your site. %sBuilt by %s%s',
'install_now' => 'Install Now',
'by' => 'by %s',
'editor_recommends' => '%s recommends %sFeedzy%s to display entries from any RSS feed with more advanced styling and filtering options.',
],
'optimole' => [
'installOptimole' => 'Install Optimole',
'gotodash' => 'Go to Optimole dashboard',
'dismisscta' => 'Dismiss this notice.',
'message1' => 'Increase this page speed and SEO ranking by optimizing images with Optimole.',
'message3' => 'Save your server space by storing images to Optimole and deliver them optimized from 400 locations around the globe. Unlimited images, Unlimited traffic.',
'message4' => 'This image looks to be too large and would affect your site speed, we recommend you to install Optimole to optimize your images.',
'message2' => 'Leverage Optimole\'s full integration with Elementor to automatically lazyload, resize, compress to AVIF/WebP and deliver from 400 locations around the globe!',
],
'redirectionCF7' => [
'gotodash' => 'Go to Contact Forms',
'dismisscta' => 'Dismiss this notice.',
'gst' => 'Get Started Free',
'message' => 'Add URL redirects, spam protection, execute JavaScript after submissions, and more with the Redirection for CF7 free plugin.',
],
'hyve' => [
'gotodash' => 'Go to Hyve Dashboard',
'install' => 'Install Hyve',
'dismisscta' => 'Dismiss this notice.',
'message' => 'Hyve is an AI-powered chatbot that turns your WordPress content into interactive conversations, helping you efficiently handle user inquiries.',
],
'wp_full_pay' => [
'gotodash' => 'Go to WP Full Pay Settings',
'install' => 'Install WP Full Pay',
'dismisscta' => 'Dismiss this notice.',
'message' => 'Enhance your donation page with WP Full Pay—create custom Stripe forms for one-time and recurring donations, manage transactions easily, and boost support with a seamless setup.',
],
],
'welcome' => [

View File

@ -338,7 +338,7 @@ class About_Us extends Abstract_Module {
'name' => 'Otter',
],
'tweet-old-post' => [
'name' => 'Revive Old Post',
'name' => 'Revive Social',
],
'feedzy-rss-feeds' => [
'name' => 'Feedzy',
@ -372,6 +372,15 @@ class About_Us extends Abstract_Module {
'name' => 'Templates Cloud',
'description' => Loader::$labels['about_us']['others']['tpc_desc'],
],
'wp-cloudflare-page-cache' => [
'name' => 'Super Page Cache',
],
'hyve-lite' => [
'name' => 'Hyve Lite',
],
'wp-full-stripe-free' => [
'name' => 'WP Full Pay',
],
];
foreach ( $products as $slug => $product ) {
@ -409,14 +418,13 @@ class About_Us extends Abstract_Module {
}
$api_data = $this->call_plugin_api( $slug );
if ( ! isset( $product['icon'] ) ) {
if ( ! isset( $product['icon'] ) && ( isset( $api_data->icons['2x'] ) || $api_data->icons['1x'] ) ) {
$products[ $slug ]['icon'] = isset( $api_data->icons['2x'] ) ? $api_data->icons['2x'] : $api_data->icons['1x'];
}
if ( ! isset( $product['description'] ) ) {
if ( ! isset( $product['description'] ) && isset( $api_data->short_description ) ) {
$products[ $slug ]['description'] = $api_data->short_description;
}
if ( ! isset( $product['name'] ) ) {
if ( ! isset( $product['name'] ) && isset( $api_data->name ) ) {
$products[ $slug ]['name'] = $api_data->name;
}
}

View File

@ -35,6 +35,17 @@ class Announcements extends Abstract_Module {
),
);
/**
* Mark is a banner for a product was already loaded.
*
* @var array
*/
private static $banner_loaded = array();
const PLUGIN_PAGE = 'https://themeisle.com/plugins';
const THEME_PAGE = 'https://themeisle.com/themes';
const REVIVE_SOCIAL = 'https://revive.social/plugins';
/**
* Holds the option prefix for the announcements.
*
@ -51,6 +62,17 @@ class Announcements extends Abstract_Module {
*/
public $time = '';
/**
* Constructor for the Announcements module.
*
* @param array $timeline Optional. An array representing the timeline of announcements. Default is an empty array.
*/
public function __construct( $timeline = array() ) {
if ( is_array( $timeline ) && ! empty( $timeline ) ) {
self::$timeline = $timeline;
}
}
/**
* Check if the module can be loaded.
*
@ -83,6 +105,7 @@ class Announcements extends Abstract_Module {
add_action( 'admin_init', array( $this, 'load_announcements' ) );
add_filter( 'themeisle_sdk_active_announcements', array( $this, 'get_active_announcements' ) );
add_filter( 'themeisle_sdk_announcements', array( $this, 'get_announcements_for_plugins' ) );
add_action( 'themeisle_sdk_load_banner', array( $this, 'load_dashboard_banner_renderer' ) );
}
/**
@ -143,9 +166,18 @@ class Announcements extends Abstract_Module {
$announcements[ $announcement ]['active'] = $this->is_active( $dates );
// Dashboard banners URLs.
$announcements[ $announcement ]['feedzy_dashboard_url'] = tsdk_utmify( 'https://themeisle.com/plugins/feedzy-rss-feeds/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['neve_dashboard_url'] = tsdk_utmify( 'https://themeisle.com/themes/neve/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['otter_dashboard_url'] = tsdk_utmify( 'https://themeisle.com/plugins/otter-blocks/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['neve_dashboard_url'] = tsdk_utmify( self::THEME_PAGE . '/neve/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['hestia_dashboard_url'] = tsdk_utmify( self::THEME_PAGE . '/hestia/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['feedzy_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/feedzy-rss-feeds/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['otter_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/otter-blocks/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['mpg_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/multi-pages-generator/blackfriday', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['ppom_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/ppom-pro/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['rfc7r_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/wpcf7-redirect/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['hyve_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/hyve/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['spc_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/super-page-cache-pro/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['visualizer_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/visualizer-charts-and-graphs/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['feedzy_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/feedzy-rss-feeds/blackfriday/', 'bfcm24', 'dashboard' );
$announcements[ $announcement ]['rop_dashboard_url'] = tsdk_utmify( self::REVIVE_SOCIAL . '/revive-old-post/', 'bfcm24', 'dashboard' );
// Customizer banners URLs.
$announcements[ $announcement ]['hestia_customizer_url'] = tsdk_utmify( 'https://themeisle.com/black-friday/', 'bfcm24', 'hestiacustomizer' );
@ -155,6 +187,22 @@ class Announcements extends Abstract_Module {
$remaining_time = $this->get_remaining_time_for_event( $dates['end'] );
$announcements[ $announcement ]['remaining_time'] = $remaining_time;
$announcements[ $announcement ]['urgency_text'] = ! empty( $remaining_time ) ? sprintf( Loader::$labels['announcements']['hurry_up'], $remaining_time ) : '';
$announcements[ $announcement ]['feedzy_banner_src'] = defined( 'FEEDZY_ABSURL' ) ? FEEDZY_ABSURL . 'img/black-friday.jpg' : '';
$announcements[ $announcement ]['visualizer_banner_src'] = defined( 'VISUALIZER_ABSURL' ) ? VISUALIZER_ABSURL . 'images/black-friday.jpg' : '';
$announcements[ $announcement ]['ppom_banner_src'] = defined( 'PPOM_URL' ) ? PPOM_URL . '/images/black-friday.jpg' : '';
$announcements[ $announcement ]['mpg_banner_src'] = defined( 'MPG_BASE_IMG_PATH' ) ? MPG_BASE_IMG_PATH . '/black-friday.jpg' : '';
$announcements[ $announcement ]['spc_banner_src'] = defined( 'SWCFPC_PLUGIN_URL' ) ? SWCFPC_PLUGIN_URL . 'assets/img/black-friday.jpg' : '';
$announcements[ $announcement ]['hestia_banner_src'] = defined( 'HESTIA_ASSETS_URL' ) ? HESTIA_ASSETS_URL . 'img/black-friday.jpg' : '';
$announcements[ $announcement ]['hyve_banner_src'] = defined( 'HYVE_LITE_URL' ) ? HYVE_LITE_URL . 'assets/images/black-friday.jpg' : '';
$announcements[ $announcement ]['rfc7r_banner_src'] = defined( 'WPCF7_PRO_REDIRECT_ASSETS_PATH' ) ? WPCF7_PRO_REDIRECT_ASSETS_PATH . 'images/black-friday.jpg' : '';
$announcements[ $announcement ]['rop_banner_src'] = defined( 'ROP_LITE_URL' ) ? ROP_LITE_URL . 'assets/img/black-friday.jpg' : '';
foreach ( $announcements[ $announcement ] as $key => $value ) {
if ( strpos( $key, '_url' ) !== false ) {
$announcements[ $announcement ][ $key ] = tsdk_translate_link( $value );
}
}
}
}
@ -277,9 +325,9 @@ class Announcements extends Abstract_Module {
foreach ( Loader::get_products() as $product ) {
$slug = $product->get_slug();
// Do not add if the contains the string 'pro'.
if ( strpos( $slug, 'pro' ) !== false ) {
continue;
// NOTE: No notice if the user has at least one Pro product.
if ( $product->requires_license() ) {
return;
}
$product_names[] = $product->get_name();
@ -299,11 +347,10 @@ class Announcements extends Abstract_Module {
<div class="themeisle-sale notice notice-info is-dismissible" data-announcement="black_friday">
<img width="24" src="<?php echo esc_url_raw( $this->get_sdk_uri() . 'assets/images/themeisle-logo.png' ); ?>"/>
<p>
<strong><?php echo esc_html( Loader::$labels['announcements']['sale_live'] ); ?> ></strong>
- <?php echo sprintf( esc_html( Loader::$labels['announcements']['max_savings'] ), esc_html( implode( ', ', $product_names ) ) ); ?>
.
<strong><?php echo esc_html( Loader::$labels['announcements']['sale_live'] ); ?></strong>
- <?php echo sprintf( esc_html( Loader::$labels['announcements']['max_savings'] ), esc_html( implode( ', ', $product_names ) ) ); ?>.
<a href="<?php echo esc_url_raw( tsdk_utmify( 'https://themeisle.com/blackfriday/', 'bfcm24', 'globalnotice' ) ); ?>"
target="_blank"><?php echo esc_html( Loader::$labels['announcements']['learn_more'] ); ?>></a>
target="_blank"><?php echo esc_html( Loader::$labels['announcements']['learn_more'] ); ?></a>
<span class="themeisle-sale-error"></span>
</p>
</div>
@ -359,4 +406,92 @@ class Announcements extends Abstract_Module {
</script>
<?php
}
/**
* Load the dashboard banner renderer.
*
* @param string $product_key The product key.
*
* @return void
*/
public function load_dashboard_banner_renderer( $product_key ) {
$banner_handler = apply_filters( 'themeisle_sdk_dependency_script_handler', 'banner' );
if ( empty( $banner_handler ) ) {
return;
}
if ( isset( self::$banner_loaded[ $product_key ] ) && true === self::$banner_loaded[ $product_key ] ) {
return;
}
self::$banner_loaded[ $product_key ] = true;
$banner_data = array();
// Get the first active banner.
foreach ( $this->get_announcements_for_plugins() as $announcement ) {
if ( false === $announcement['active'] ) {
continue;
}
$cta_key = $product_key . '_dashboard_url';
$banner_src_key = $product_key . '_banner_src';
if (
! isset( $announcement[ $cta_key ] ) ||
! isset( $announcement[ $banner_src_key ] ) ||
empty( $announcement[ $banner_src_key ] ) ||
! isset( $announcement['urgency_text'] )
) {
continue;
}
$banner_data = array(
'content' => $this->render_banner(
array(
'cta_url' => $announcement[ $cta_key ],
'img_src' => $announcement[ $banner_src_key ],
'urgency_text' => $announcement['urgency_text'],
)
),
);
break;
}
if ( empty( $banner_data ) ) {
return;
}
do_action( 'themeisle_sdk_dependency_enqueue_script', 'banner' );
wp_localize_script( $banner_handler, 'tsdk_banner_data', $banner_data );
}
/**
* Renders a banner with the provided settings.
*
* @param array $settings {
* Optional. An array of settings for the banner.
*
* @type string $cta_url The URL for the call-to-action link.
* @type string $img_src The source URL for the banner image.
* @type string $urgency_text The urgency text to display on the banner.
* }
* @return string The HTML output of the banner.
*/
public function render_banner( $settings = array() ) {
if ( empty( $settings ) ) {
return '';
}
return wp_kses_post(
wp_sprintf(
'<a href="%s" target="_blank" class="tsdk-banner-cta"><img src="%s" class="tsdk-banner-img"><div class="tsdk-banner-urgency-text">%s</div></a>',
esc_url_raw( $settings['cta_url'] ),
esc_url_raw( $settings['img_src'] ),
sanitize_text_field( $settings['urgency_text'] )
)
);
}
}

View File

@ -82,7 +82,6 @@ class Dashboard_Widget extends Abstract_Module {
'themeisle_sdk_dashboard_widget_feeds',
[
'https://themeisle.com/blog/feed',
'https://www.codeinwp.com/blog/feed',
'https://wpshout.com/feed',
]
);

View File

@ -926,7 +926,7 @@ class Licenser extends Abstract_Module {
*/
public function can_load( $product ) {
if ( $product->is_wordpress_available() ) {
if ( ! $product->requires_license() ) {
return false;
}
@ -975,55 +975,59 @@ class Licenser extends Abstract_Module {
add_action( 'admin_head', [ $this, 'auto_activate' ] );
if ( $this->product->is_plugin() ) {
add_filter(
'pre_set_site_transient_update_plugins',
[
$this,
'pre_set_site_transient_update_plugins_filter',
]
);
add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
add_filter( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
'http_request_args',
array(
$this,
'http_request_args',
),
10,
2
);
if ( ! self::is_valid( $product->get_basefile() ) ) {
if ( ! $product->is_wordpress_available() ) {
add_filter(
'plugin_action_links_' . plugin_basename( $product->get_basefile() ),
function ( $actions ) {
if ( $this->get_license_status( true ) !== self::STATUS_ACTIVE_EXPIRED ) {
return $actions;
}
$new_actions['deactivate'] = $actions['deactivate'];
$new_actions['renew_link'] = '<a style="color:#d63638" href="' . esc_url( $this->renew_url() ) . '" target="_blank" rel="external noopener noreferrer">' . esc_html( Loader::$labels['licenser']['renew_cta'] ) . '</a>';
return $new_actions;
}
'pre_set_site_transient_update_plugins',
[
$this,
'pre_set_site_transient_update_plugins_filter',
]
);
add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
add_filter( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
'http_request_args',
array(
$this,
'http_request_args',
),
10,
2
);
if ( ! self::is_valid( $product->get_basefile() ) ) {
add_filter(
'plugin_action_links_' . plugin_basename( $product->get_basefile() ),
function ( $actions ) {
if ( $this->get_license_status( true ) !== self::STATUS_ACTIVE_EXPIRED ) {
return $actions;
}
$new_actions['deactivate'] = $actions['deactivate'];
$new_actions['renew_link'] = '<a style="color:#d63638" href="' . esc_url( $this->renew_url() ) . '" target="_blank" rel="external noopener noreferrer">' . esc_html( Loader::$labels['licenser']['renew_cta'] ) . '</a>';
return $new_actions;
}
);
}
}
return $this;
}
if ( $this->product->is_theme() ) {
add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
add_action( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
add_filter( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
'http_request_args',
array(
$this,
'disable_wporg_update',
),
5,
2
);
if ( ! $product->is_wordpress_available() ) {
add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
add_action( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
add_filter( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
'http_request_args',
array(
$this,
'disable_wporg_update',
),
5,
2
);
}
return $this;

View File

@ -79,12 +79,18 @@ class Logger extends Abstract_Module {
if ( ! $this->is_logger_active() ) {
return;
}
add_action(
'admin_enqueue_scripts',
function() {
if ( ! apply_filters( 'themeisle_sdk_enable_telemetry', false ) ) {
return;
}
$can_load_telemetry = apply_filters( 'themeisle_sdk_enable_telemetry', false );
if ( $can_load_telemetry ) {
add_action( 'admin_enqueue_scripts', array( $this, 'load_telemetry' ) );
}
$this->load_telemetry();
},
PHP_INT_MAX
);
$action_key = $this->product->get_key() . '_log_activity';
if ( ! wp_next_scheduled( $action_key ) ) {

View File

@ -32,7 +32,7 @@ class Promotions extends Abstract_Module {
*
* @var array
*/
private $promotions = array();
public $promotions = array();
/**
* Holds the values of the promotions that are not allowed to be shown.
@ -71,11 +71,39 @@ class Promotions extends Abstract_Module {
private $option_rop = 'themeisle_sdk_promotions_rop_installed';
/**
* Option key for Neve FSE promos.
* Option key for Neve promo.
*
* @var string
*/
private $option_neve_fse = 'themeisle_sdk_promotions_neve_fse_installed';
private $option_neve = 'themeisle_sdk_promotions_neve_installed';
/**
* Option key for Redirection for CF7.
*
* @var string
*/
private $option_redirection_cf7 = 'themeisle_sdk_promotions_redirection_cf7_installed';
/**
* Option key for Hyve.
*
* @var string
*/
private $option_hyve = 'themeisle_sdk_promotions_hyve_installed';
/**
* Option key for WP Full Pay.
*
* @var string
*/
private $option_wp_full_pay = 'themeisle_sdk_promotions_wp_full_pay_installed';
/**
* Option key for Feedzy.
*
* @var string
*/
private $option_feedzy = 'themeisle_sdk_promotions_feedzy_installed';
/**
* Loaded promotion.
@ -113,12 +141,22 @@ class Promotions extends Abstract_Module {
return false;
}
$this->debug = apply_filters( 'themeisle_sdk_promo_debug', $this->debug );
$promotions_to_load = apply_filters( $product->get_key() . '_load_promotions', array() );
$this->debug = apply_filters( 'themeisle_sdk_promo_debug', $this->debug );
$promotions_to_load = apply_filters( $product->get_key() . '_load_promotions', array() );
$promotions_to_load[] = 'optimole';
$promotions_to_load[] = 'rop';
$promotions_to_load[] = 'woo_plugins';
$promotions_to_load[] = 'neve-fse';
$promotions_to_load[] = 'neve';
$promotions_to_load[] = 'redirection-cf7';
$promotions_to_load[] = 'hyve';
$promotions_to_load[] = 'wp_full_pay';
$promotions_to_load[] = 'feedzy_import';
if ( defined( 'NEVE_VERSION' ) || defined( 'WPMM_PATH' ) || defined( 'OTTER_BLOCKS_VERSION' ) || defined( 'OBFX_URL' ) ) {
$promotions_to_load[] = 'feedzy_embed';
}
$promotions_to_load = array_unique( $promotions_to_load );
$this->promotions = $this->get_promotions();
@ -129,7 +167,6 @@ class Promotions extends Abstract_Module {
unset( $this->promotions[ $slug ] );
}
}
add_action( 'init', array( $this, 'register_settings' ), 99 );
add_action( 'admin_init', array( $this, 'register_reference' ), 99 );
@ -146,21 +183,21 @@ class Promotions extends Abstract_Module {
return;
}
$this->product = $product;
$last_dismiss_time = $this->get_last_dismiss_time();
$last_dismiss = $this->get_last_dismiss_time();
if ( ! $this->debug && $last_dismiss && ( time() - $last_dismiss ) < 7 * DAY_IN_SECONDS ) {
if ( ! $this->debug && is_int( $last_dismiss_time ) && ( time() - $last_dismiss_time ) < ( 3 * WEEK_IN_SECONDS ) ) {
return;
}
$this->product = $product;
add_filter( 'attachment_fields_to_edit', array( $this, 'add_attachment_field' ), 10, 2 );
add_action( 'current_screen', [ $this, 'load_available' ] );
add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue' ) );
add_action( 'wp_ajax_tisdk_update_option', array( $this, 'dismiss_promotion' ) );
add_filter( 'themeisle_sdk_ran_promos', '__return_true' );
if ( get_option( $this->option_neve_fse, false ) !== true ) {
if ( get_option( $this->option_neve, false ) !== true ) {
add_action( 'wp_ajax_themeisle_sdk_dismiss_notice', 'ThemeisleSDK\Modules\Notification::regular_dismiss' );
}
}
@ -209,8 +246,20 @@ class Promotions extends Abstract_Module {
update_option( 'rop_reference_key', sanitize_key( $_GET['rop_reference_key'] ) );
}
if ( isset( $_GET['neve_fse_reference_key'] ) ) {
update_option( 'neve_fse_reference_key', sanitize_key( $_GET['neve_fse_reference_key'] ) );
if ( isset( $_GET['neve_reference_key'] ) ) {
update_option( 'neve_reference_key', sanitize_key( $_GET['neve_reference_key'] ) );
}
if ( isset( $_GET['hyve_reference_key'] ) ) {
update_option( 'hyve_reference_key', sanitize_key( $_GET['hyve_reference_key'] ) );
}
if ( isset( $_GET['wp_full_pay_reference_key'] ) ) {
update_option( 'wp_full_pay_reference_key', sanitize_key( $_GET['wp_full_pay_reference_key'] ) );
}
if ( isset( $_GET['feedzy_reference_key'] ) || ( isset( $_GET['from'], $_GET['plugin'] ) && $_GET['from'] === 'import' && str_starts_with( sanitize_key( $_GET['plugin'] ), 'feedzy' ) ) ) {
update_option( 'feedzy_reference_key', sanitize_key( $_GET['feedzy_reference_key'] ?? 'i-' . $this->product->get_key() ) );
update_option( $this->option_feedzy, 1 );
}
}
@ -263,7 +312,37 @@ class Promotions extends Abstract_Module {
);
register_setting(
'themeisle_sdk_settings',
$this->option_neve_fse,
$this->option_neve,
array(
'type' => 'boolean',
'sanitize_callback' => 'rest_sanitize_boolean',
'show_in_rest' => true,
'default' => false,
)
);
register_setting(
'themeisle_sdk_settings',
$this->option_redirection_cf7,
array(
'type' => 'boolean',
'sanitize_callback' => 'rest_sanitize_boolean',
'show_in_rest' => true,
'default' => false,
)
);
register_setting(
'themeisle_sdk_settings',
$this->option_hyve,
array(
'type' => 'boolean',
'sanitize_callback' => 'rest_sanitize_boolean',
'show_in_rest' => true,
'default' => false,
)
);
register_setting(
'themeisle_sdk_settings',
$this->option_wp_full_pay,
array(
'type' => 'boolean',
'sanitize_callback' => 'rest_sanitize_boolean',
@ -313,31 +392,45 @@ class Promotions extends Abstract_Module {
* @return array
*/
private function get_promotions() {
$has_otter = defined( 'OTTER_BLOCKS_VERSION' ) || $this->is_plugin_installed( 'otter-blocks' );
$had_otter_from_promo = get_option( $this->option_otter, false );
$has_optimole = defined( 'OPTIMOLE_VERSION' ) || $this->is_plugin_installed( 'optimole-wp' );
$had_optimole_from_promo = get_option( $this->option_optimole, false );
$has_rop = defined( 'ROP_LITE_VERSION' ) || $this->is_plugin_installed( 'tweet-old-post' );
$had_rop_from_promo = get_option( $this->option_rop, false );
$has_woocommerce = class_exists( 'WooCommerce' );
$has_sparks = defined( 'SPARKS_WC_VERSION' ) || $this->is_plugin_installed( 'sparks-for-woocommerce' );
$has_ppom = defined( 'PPOM_VERSION' ) || $this->is_plugin_installed( 'woocommerce-product-addon' );
$is_min_req_v = version_compare( get_bloginfo( 'version' ), '5.8', '>=' );
$is_min_fse_v = version_compare( get_bloginfo( 'version' ), '6.2', '>=' );
$current_theme = wp_get_theme();
$has_neve_fse = $current_theme->template === 'neve-fse' || $current_theme->parent() === 'neve-fse';
$has_enough_attachments = $this->has_min_media_attachments();
$has_enough_old_posts = $this->has_old_posts();
$has_otter = defined( 'OTTER_BLOCKS_VERSION' ) || $this->is_plugin_installed( 'otter-blocks' );
$had_otter_from_promo = get_option( $this->option_otter, false );
$has_optimole = defined( 'OPTIMOLE_VERSION' ) || $this->is_plugin_installed( 'optimole-wp' );
$had_optimole_from_promo = get_option( $this->option_optimole, false );
$has_rop = defined( 'ROP_LITE_VERSION' ) || $this->is_plugin_installed( 'tweet-old-post' );
$had_rop_from_promo = get_option( $this->option_rop, false );
$has_woocommerce = class_exists( 'WooCommerce' );
$has_sparks = defined( 'SPARKS_WC_VERSION' ) || $this->is_plugin_installed( 'sparks-for-woocommerce' );
$has_ppom = defined( 'PPOM_VERSION' ) || $this->is_plugin_installed( 'woocommerce-product-addon' );
$has_redirection_cf7 = defined( 'WPCF7_PRO_REDIRECT_PLUGIN_VERSION' ) || $this->is_plugin_installed( 'wpcf7-redirect' );
$had_redirection_cf7_promo = get_option( $this->option_redirection_cf7, false );
$has_hyve = defined( 'HYVE_LITE_VERSION' ) || $this->is_plugin_installed( 'hyve' ) || $this->is_plugin_installed( 'hyve-lite' );
$had_hyve_from_promo = get_option( $this->option_hyve, false );
$has_hyve_conditions = version_compare( get_bloginfo( 'version' ), '6.2', '>=' ) && $this->has_support_page();
$has_wfp_full_pay = defined( 'WP_FULL_STRIPE_BASENAME' ) || $this->is_plugin_installed( 'wp-full-stripe-free' );
$had_wfp_from_promo = get_option( $this->option_wp_full_pay, false );
$has_wfp_conditions = $this->has_donate_page();
$is_min_req_v = version_compare( get_bloginfo( 'version' ), '5.8', '>=' );
$current_theme = wp_get_theme();
$has_neve = $current_theme->template === 'neve' || $current_theme->parent() === 'neve';
$has_neve_from_promo = get_option( $this->option_neve, false );
$has_enough_attachments = $this->has_min_media_attachments();
$has_enough_old_posts = $this->has_old_posts();
$is_min_php_8_1 = version_compare( PHP_VERSION, '8.1', '>=' );
$has_feedzy = defined( 'FEEDZY_BASEFILE' ) || $this->is_plugin_installed( 'feedzy-rss-feedss' );
$had_feedzy_from_promo = get_option( $this->option_feedzy, false );
$all = [
'optimole' => [
'optimole' => [
'om-editor' => [
'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo,
'screen' => 'editor',
'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo,
'screen' => 'editor',
'delayed' => true,
],
'om-image-block' => [
'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo,
'screen' => 'editor',
'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo,
'screen' => 'editor',
'delayed' => true,
],
'om-attachment' => [
'env' => ! $has_optimole && ! $had_optimole_from_promo,
@ -348,31 +441,49 @@ class Promotions extends Abstract_Module {
'screen' => 'media',
],
'om-elementor' => [
'env' => ! $has_optimole && ! $had_optimole_from_promo && defined( 'ELEMENTOR_VERSION' ),
'screen' => 'elementor',
'env' => ! $has_optimole && ! $had_optimole_from_promo && defined( 'ELEMENTOR_VERSION' ),
'screen' => 'elementor',
'delayed' => true,
],
],
'otter' => [
'blocks-css' => [
'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
'feedzy_import' => [
'feedzy-import' => [
'env' => true,
'screen' => 'import',
'always' => true,
],
],
'feedzy_embed' => [
'feedzy-editor' => [
'env' => ! $has_feedzy && is_main_site() && ! $had_feedzy_from_promo,
'screen' => 'editor',
],
],
'otter' => [
'blocks-css' => [
'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
'screen' => 'editor',
'delayed' => true,
],
'blocks-animation' => [
'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
'screen' => 'editor',
'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
'screen' => 'editor',
'delayed' => true,
],
'blocks-conditions' => [
'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
'screen' => 'editor',
'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
'screen' => 'editor',
'delayed' => true,
],
],
'rop' => [
'rop' => [
'rop-posts' => [
'env' => ! $has_rop && ! $had_rop_from_promo && $has_enough_old_posts,
'screen' => 'edit-post',
'env' => ! $has_rop && ! $had_rop_from_promo && $has_enough_old_posts,
'screen' => 'edit-post',
'delayed' => true,
],
],
'woo_plugins' => [
'woo_plugins' => [
'ppom' => [
'env' => ! $has_ppom && $has_woocommerce,
'screen' => 'edit-product',
@ -390,12 +501,31 @@ class Promotions extends Abstract_Module {
'screen' => 'edit-product',
],
],
'neve-fse' => [
'neve-fse-themes-popular' => [
'env' => ! $has_neve_fse && $is_min_fse_v,
'neve' => [
'neve-themes-popular' => [
'env' => ! $has_neve && ! $has_neve_from_promo,
'screen' => 'themes-install-popular',
],
],
'redirection-cf7' => [
'wpcf7' => [
'env' => ! $has_redirection_cf7 && ! $had_redirection_cf7_promo,
'screen' => 'wpcf7',
'delayed' => true,
],
],
'hyve' => [
'hyve-plugins-install' => [
'env' => $is_min_php_8_1 && ! $has_hyve && ! $had_hyve_from_promo && $has_hyve_conditions,
'screen' => 'plugin-install',
],
],
'wp_full_pay' => [
'wp-full-pay-plugins-install' => [
'env' => ! $has_wfp_full_pay && ! $had_wfp_from_promo && $has_wfp_conditions,
'screen' => 'plugin-install',
],
],
];
foreach ( $all as $slug => $data ) {
@ -415,7 +545,6 @@ class Promotions extends Abstract_Module {
unset( $all[ $slug ] );
}
}
return $all;
}
@ -442,18 +571,12 @@ class Promotions extends Abstract_Module {
/**
* Get the last dismiss time of a promotion.
*
* @return int The timestamp of last dismiss, or install time - 4 days.
* @return int | false The timestamp of last dismiss or false.
*/
private function get_last_dismiss_time() {
$dismissed = $this->get_upsells_dismiss_time();
if ( empty( $dismissed ) ) {
// we return the product install time - 4 days because we want to show the upsell after 3 days,
// and we move the product install time 4 days in the past.
return $this->product->get_install_time() - 4 * DAY_IN_SECONDS;
}
return max( array_values( $dismissed ) );
return empty( $dismissed ) ? false : max( array_values( $dismissed ) );
}
/**
@ -464,17 +587,43 @@ class Promotions extends Abstract_Module {
private function filter_by_screen_and_merge() {
$current_screen = get_current_screen();
$is_elementor = isset( $_GET['action'] ) && $_GET['action'] === 'elementor';
$is_media = isset( $current_screen->id ) && $current_screen->id === 'upload';
$is_posts = isset( $current_screen->id ) && $current_screen->id === 'edit-post';
$is_editor = method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor();
$is_theme_install = isset( $current_screen->id ) && ( $current_screen->id === 'theme-install' || $current_screen->id === 'themes' );
$is_product = isset( $current_screen->id ) && $current_screen->id === 'product';
$return = [];
$is_elementor = isset( $_GET['action'] ) && $_GET['action'] === 'elementor';
$is_media = isset( $current_screen->id ) && $current_screen->id === 'upload';
$is_posts = isset( $current_screen->id ) && $current_screen->id === 'edit-post';
$is_editor = method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor();
$is_theme_install = isset( $current_screen->id ) && ( $current_screen->id === 'theme-install' );
$is_plugin_install = isset( $current_screen->id ) && ( $current_screen->id === 'plugin-install' );
$is_product = isset( $current_screen->id ) && $current_screen->id === 'product';
$is_import = isset( $current_screen->id ) && $current_screen->id === 'import';
$is_cf7_install = isset( $current_screen->id ) && function_exists( 'str_contains' ) ? str_contains( $current_screen->id, 'page_wpcf7' ) : false;
$return = [];
$product_install_time = (int) $this->product->get_install_time();
$is_older = time() > ( $product_install_time + ( 3 * DAY_IN_SECONDS ) );
$is_newer = time() < ( $product_install_time + ( 6 * HOUR_IN_SECONDS ) );
foreach ( $this->promotions as $slug => $promos ) {
foreach ( $promos as $key => $data ) {
$data = wp_parse_args(
$data,
[
'delayed' => false,
'always' => false,
]
);
if (
! $this->debug &&
(
( $data['delayed'] === true && ! $is_older ) || // Skip promotions that are delayed for 3 days.
$is_newer // Skip promotions for the first 6 hours after install.
)
&& ! $data['always']
) {
unset( $this->promotions[ $slug ][ $key ] );
continue;
}
switch ( $data['screen'] ) {
case 'media-editor':
if ( ! $is_media && ! $is_editor ) {
@ -491,6 +640,11 @@ class Promotions extends Abstract_Module {
unset( $this->promotions[ $slug ][ $key ] );
}
break;
case 'import':
if ( ! $is_import ) {
unset( $this->promotions[ $slug ][ $key ] );
}
break;
case 'elementor':
if ( ! $is_elementor ) {
unset( $this->promotions[ $slug ][ $key ] );
@ -511,6 +665,16 @@ class Promotions extends Abstract_Module {
unset( $this->promotions[ $slug ][ $key ] );
}
break;
case 'wpcf7':
if ( ! $is_cf7_install ) {
unset( $this->promotions[ $slug ][ $key ] );
}
break;
case 'plugin-install':
if ( ! $is_plugin_install ) {
unset( $this->promotions[ $slug ][ $key ] );
}
break;
}
}
@ -545,15 +709,25 @@ class Promotions extends Abstract_Module {
if ( $this->get_upsells_dismiss_time( 'rop-posts' ) === false ) {
add_action( 'admin_notices', [ $this, 'render_rop_dash_notice' ] );
}
if ( $this->get_upsells_dismiss_time( 'neve-fse-themes-popular' ) === false ) {
add_action( 'admin_notices', [ $this, 'render_neve_fse_themes_notice' ] );
if ( $this->get_upsells_dismiss_time( 'neve-themes-popular' ) === false ) {
add_action( 'admin_notices', [ $this, 'render_neve_themes_notice' ] );
}
if ( $this->get_upsells_dismiss_time( 'redirection-cf7' ) === false ) {
add_action( 'admin_notices', [ $this, 'render_redirection_cf7_notice' ] );
}
if ( $this->get_upsells_dismiss_time( 'hyve-plugins-install' ) === false ) {
add_action( 'admin_notices', [ $this, 'render_hyve_notice' ] );
}
if ( $this->get_upsells_dismiss_time( 'wp-full-pay-plugins-install' ) === false ) {
add_action( 'admin_notices', [ $this, 'render_wp_full_pay_notice' ] );
}
add_action( 'load-import.php', [ $this, 'add_import' ] );
$this->load_woo_promos();
return;
}
switch ( $slug ) {
case 'om-editor':
case 'om-image-block':
@ -573,14 +747,21 @@ class Promotions extends Abstract_Module {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
add_action( 'admin_notices', [ $this, 'render_rop_dash_notice' ] );
break;
case 'feedzy-import':
add_action( 'load-import.php', [ $this, 'add_import' ] );
break;
case 'feedzy-editor':
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue' ] );
break;
case 'ppom':
case 'sparks-wishlist':
case 'sparks-announcement':
case 'sparks-product-reviews':
$this->load_woo_promos();
break;
case 'neve-fse-themes-popular':
// Remove any other notifications if Neve FSE promotion is showing
case 'neve-themes-popular':
// Remove any other notifications if Neve promotion is showing
remove_action( 'admin_notices', array( 'ThemeisleSDK\Modules\Notification', 'show_notification' ) );
remove_action(
'wp_ajax_themeisle_sdk_dismiss_notice',
@ -593,11 +774,42 @@ class Promotions extends Abstract_Module {
remove_action( 'admin_head', array( 'ThemeisleSDK\Modules\Notification', 'setup_notifications' ) );
// Add required actions to display this notification
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
add_action( 'admin_notices', [ $this, 'render_neve_fse_themes_notice' ] );
add_action( 'admin_notices', [ $this, 'render_neve_themes_notice' ] );
break;
case 'wpcf7':
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
add_action( 'admin_notices', [ $this, 'render_redirection_cf7_notice' ] );
break;
case 'hyve-plugins-install':
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
add_action( 'admin_notices', [ $this, 'render_hyve_notice' ] );
break;
case 'wp-full-pay-plugins-install':
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
add_action( 'admin_notices', [ $this, 'render_wp_full_pay_notice' ] );
break;
}
}
/**
* Add import row.
*
* @return void
*/
public function add_import() {
global $wp_importers;
if ( isset( $wp_importers['feedzy-rss-feeds'] ) ) {
return;
}
$wp_importers['feedzy-rss-feeds'] = array( // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
'Feedzy',
sprintf( Loader::$labels['promotions']['feedzy']['import_desc'], '<span style="float: left; font-style: italic;margin-top:0.4em;">', $this->product->get_friendly_name(), '</span>' ),
'install' => 'feedzy-rss-feeds',
);
if ( defined( 'FEEDZY_BASEFILE' ) ) {
unset( $wp_importers['feedzy-rss-feeds']['install'] );
}
}
/**
* Render dashboard notice.
*/
@ -623,31 +835,64 @@ class Promotions extends Abstract_Module {
$asset_file = require $themeisle_sdk_max_path . '/assets/js/build/promos/index.asset.php';
$deps = array_merge( $asset_file['dependencies'], [ 'updates' ] );
$themes = wp_get_themes();
$neve_action = isset( $themes['neve'] ) ? 'activate' : 'install';
$labels = Loader::$labels['promotions'];
$labels['feedzy']['editor_recommends'] = sprintf(
$labels['feedzy']['editor_recommends'],
$this->product->get_friendly_name(),
'<a target="_blank" href="' . add_query_arg(
array(
'tab' => 'plugin-information',
'plugin' => 'feedzy-rss-feeds',
'_wpnonce' => wp_create_nonce( 'activate-plugin_feedzy-rss-feeds' ),
'feedzy_reference_key' => 'e-' . $this->product->get_key(),
),
network_admin_url( 'plugin-install.php' )
) . '">',
'</a>'
);
wp_register_script( $handle, $themeisle_sdk_src . 'assets/js/build/promos/index.js', $deps, $asset_file['version'], true );
wp_localize_script(
$handle,
'themeisleSDKPromotions',
[
'debug' => $this->debug,
'labels' => [
'optimole' => Loader::$labels['promotions']['optimole'],
],
'email' => $user->user_email,
'showPromotion' => $this->loaded_promo,
'optionKey' => $this->option_main,
'product' => $this->product->get_name(),
'option' => empty( $saved ) ? new \stdClass() : $saved,
'nonce' => wp_create_nonce( 'wp_rest' ),
'assets' => $themeisle_sdk_src . 'assets/images/',
'optimoleApi' => esc_url( rest_url( 'optml/v1/register_service' ) ),
'optimoleActivationUrl' => $this->get_plugin_activation_link( 'optimole-wp' ),
'otterActivationUrl' => $this->get_plugin_activation_link( 'otter-blocks' ),
'ropActivationUrl' => $this->get_plugin_activation_link( 'tweet-old-post' ),
'optimoleDash' => esc_url( add_query_arg( [ 'page' => 'optimole' ], admin_url( 'upload.php' ) ) ),
'ropDash' => esc_url( add_query_arg( [ 'page' => 'TweetOldPost' ], admin_url( 'admin.php' ) ) ),
'neveFSEMoreUrl' => tsdk_utmify( 'https://themeisle.com/themes/neve-fse/', 'neve-fse-themes-popular', 'theme-install' ),
'debug' => $this->debug,
'labels' => $labels,
'email' => $user->user_email,
'showPromotion' => $this->loaded_promo,
'optionKey' => $this->option_main,
'product' => $this->product->get_name(),
'option' => empty( $saved ) ? new \stdClass() : $saved,
'nonce' => wp_create_nonce( 'wp_rest' ),
'assets' => $themeisle_sdk_src . 'assets/images/',
'optimoleApi' => esc_url( rest_url( 'optml/v1/register_service' ) ),
'optimoleActivationUrl' => $this->get_plugin_activation_link( 'optimole-wp' ),
'otterActivationUrl' => $this->get_plugin_activation_link( 'otter-blocks' ),
'ropActivationUrl' => $this->get_plugin_activation_link( 'tweet-old-post' ),
'optimoleDash' => esc_url( add_query_arg( [ 'page' => 'optimole' ], admin_url( 'upload.php' ) ) ),
'ropDash' => esc_url( add_query_arg( [ 'page' => 'TweetOldPost' ], admin_url( 'admin.php' ) ) ),
// translators: %s is the product name.
'title' => esc_html( sprintf( Loader::$labels['promotions']['recommended'], $this->product->get_name() ) ),
'title' => esc_html( sprintf( Loader::$labels['promotions']['recommended'], $this->product->get_name() ) ),
'redirectionCF7MoreUrl' => tsdk_utmify( 'https://docs.themeisle.com/collection/2014-redirection-for-contact-form-7', 'redirection-for-contact-form-7', 'plugin-install' ),
'rfCF7ActivationUrl' => $this->get_plugin_activation_link( 'wpcf7-redirect' ),
'cf7Dash' => esc_url( add_query_arg( [ 'page' => 'wpcf7-new' ], admin_url( 'admin.php' ) ) ),
'hyveActivationUrl' => $this->get_plugin_activation_link( 'hyve-lite' ),
'hyveDash' => esc_url( add_query_arg( [ 'page' => 'wpfs-settings-stripe' ], admin_url( 'admin.php' ) ) ),
'wpFullPayActivationUrl' => $this->get_plugin_activation_link( 'wp-full-stripe-free' ),
'wpFullPayDash' => esc_url( add_query_arg( [ 'page' => 'wpfs-settings-stripe' ], admin_url( 'admin.php' ) ) ),
'nevePreviewURL' => esc_url( add_query_arg( [ 'theme' => 'neve' ], admin_url( 'theme-install.php' ) ) ),
'neveAction' => $neve_action,
'activateNeveURL' => esc_url(
add_query_arg(
[
'action' => 'activate',
'stylesheet' => 'neve',
'_wpnonce' => wp_create_nonce( 'switch-theme_neve' ),
],
admin_url( 'themes.php' )
)
),
]
);
wp_enqueue_script( $handle );
@ -668,10 +913,31 @@ class Promotions extends Abstract_Module {
}
/**
* Render Neve FSE Themes notice.
* Render Neve Themes notice.
*/
public function render_neve_fse_themes_notice() {
echo '<div id="ti-neve-fse-notice" class="notice notice-info ti-sdk-neve-fse-notice"></div>';
public function render_neve_themes_notice() {
echo '<div id="ti-neve-notice" class="notice notice-info ti-sdk-om-notice"></div>';
}
/**
* Render Hyve notice.
*/
public function render_hyve_notice() {
echo '<div id="ti-hyve-notice" class="notice notice-info ti-sdk-om-notice"></div>';
}
/**
* Render WP Full Pay notice.
*/
public function render_wp_full_pay_notice() {
echo '<div id="ti-wp-full-pay-notice" class="notice notice-info ti-sdk-om-notice"></div>';
}
/**
* Render Redirection for CF7 notice.
*/
public function render_redirection_cf7_notice() {
echo '<div id="ti-redirection-cf7-notice" class="notice notice-info ti-sdk-om-notice"></div>';
}
/**
@ -693,7 +959,7 @@ class Promotions extends Abstract_Module {
$meta = wp_get_attachment_metadata( $post->ID );
if ( isset( $meta['filesize'] ) && $meta['filesize'] < 200000 ) {
if ( isset( $meta['filesize'] ) && $meta['filesize'] < 100000 ) {
return $fields;
}
@ -923,7 +1189,6 @@ class Promotions extends Abstract_Module {
content: url("data:image/svg+xml,%3Csvg fill='%23135e96' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='<?php echo esc_attr( $icon ); ?>'/%3E%3C/svg%3E") !important;
min-width: 13px;
max-width: 13px;
margin: auto;
}
.tisdk-suggestions_options.active a::before {
@ -1084,4 +1349,61 @@ class Promotions extends Abstract_Module {
wp_send_json( $response );
wp_die();
}
/**
* Check if the user has a support page.
*/
public function has_support_page() {
$transient_name = 'tisdk_has_support_page';
$has_support = get_transient( $transient_name );
if ( false === $has_support ) {
global $wpdb;
// We use %i escape identifier that was added in WP 6.2.0, hence need to ignore PHPCS warning.
// We only show this notice to users on higher version as that is the minimum for Hyve as well.
$query = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
'SELECT ID FROM %i WHERE post_type = %s AND post_status = %s AND post_title LIKE %s LIMIT 1', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnsupportedPlaceholder
$wpdb->posts,
'page',
'publish',
'%support%'
)
);
$has_support = $query ? 'yes' : 'no';
set_transient( $transient_name, $has_support, 7 * DAY_IN_SECONDS );
}
return 'yes' === $has_support;
}
/**
* Check if the user has a donate page.
*/
public function has_donate_page() {
$transient_name = 'tisdk_has_donate_page';
$has_donate = get_transient( $transient_name );
if ( false === $has_donate ) {
global $wpdb;
$query = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
'SELECT ID FROM ' . $wpdb->posts . ' WHERE post_type = %s AND post_status = %s AND post_title LIKE %s LIMIT 1',
'page',
'publish',
'%donate%'
)
);
$has_donate = $query ? 'yes' : 'no';
set_transient( $transient_name, $has_donate, 7 * DAY_IN_SECONDS );
}
return 'yes' === $has_donate;
}
}

View File

@ -54,16 +54,10 @@ class Review extends Abstract_Module {
public function add_notification( $all_notifications ) {
$developers = [
'Bogdan',
'Marius',
'Hardeep',
'Rodica',
'Stefan',
'Uriahs',
'Madalin',
'Cristi',
'Silviu',
'Andrei',
'Robert',
];
$link = 'https://wordpress.org/support/' . $this->product->get_type() . '/' . $this->product->get_slug() . '/reviews/#wporg-footer';
@ -76,7 +70,7 @@ class Review extends Abstract_Module {
[ '{product}', '{developer}' ],
[
$this->product->get_friendly_name(),
$developers[ strlen( get_site_url() ) % 10 ],
$developers[ strlen( get_site_url() ) % count( $developers ) ],
],
$message
);

View File

@ -62,8 +62,28 @@ class Script_Loader extends Abstract_Module {
add_filter( 'themeisle_sdk_dependency_script_handler', [ $this, 'get_script_handler' ], 10, 1 );
add_action( 'themeisle_sdk_dependency_enqueue_script', [ $this, 'enqueue_script' ], 10, 1 );
add_filter( 'themeisle_sdk_secret_masking', [ $this, 'secret_masking' ], 10, 1 );
add_filter( 'themeisle_sdk_script_setup', '__return_true' );
add_action( 'themeisle_internal_page', [ $this, 'load_survey_for_product' ], 10, 2 );
}
/**
* Load survey for product using internal pages.
*
* @param string $product_slug Product slug.
* @param string $page_slug Page slug.
*/
public function load_survey_for_product( $product_slug, $page_slug ) {
$data = apply_filters( 'themeisle-sdk/survey/' . $product_slug, [], $page_slug );
if ( empty( $data ) || ! is_array( $data ) ) {
return;
}
$handler = $this->get_script_handler( 'survey' );
$this->load_survey( $handler, $data );
}
/**
@ -78,7 +98,7 @@ class Script_Loader extends Abstract_Module {
return '';
}
if ( 'tracking' !== $slug && 'survey' !== $slug ) {
if ( 'tracking' !== $slug && 'survey' !== $slug && 'banner' !== $slug ) {
return '';
}
@ -100,6 +120,8 @@ class Script_Loader extends Abstract_Module {
$this->load_tracking( $handler );
} elseif ( 'survey' === $slug ) {
$this->load_survey( $handler );
} elseif ( 'banner' === $slug ) {
$this->load_banner( $handler );
}
}
@ -107,10 +129,11 @@ class Script_Loader extends Abstract_Module {
* Load the survey script.
*
* @param string $handler The script handler.
* @param array $data The survey data.
*
* @return void
*/
public function load_survey( $handler ) {
public function load_survey( $handler, $data = array() ) {
global $themeisle_sdk_max_path;
$asset_file = require $themeisle_sdk_max_path . '/assets/js/build/survey/survey_deps.asset.php';
@ -121,6 +144,77 @@ class Script_Loader extends Abstract_Module {
$asset_file['version'],
true
);
$data = array_replace_recursive( $this->get_survey_common_data( $data ), $data );
wp_localize_script( $handler, 'tsdk_survey_data', $data );
}
/**
* Get the common data in the Formbrick survey format.
*
* @param array $reference_data Reference data to extrapolate common properties.
*
* @return array
*/
public function get_survey_common_data( $reference_data = array() ) {
$language = apply_filters( 'themeisle_sdk_current_lang', get_user_locale() );
$available_languages = [
'de_DE' => 'de',
'de_DE_formal' => 'de',
];
$lang_code = isset( $available_languages[ $language ] ) ? $available_languages[ $language ] : 'en';
$url_parts = wp_parse_url( apply_filters( 'themeisle_sdk_current_site_url', get_site_url() ) );
$clean_url = str_replace( 'www.', '', $url_parts['host'] );
if ( isset( $url_parts['path'] ) ) {
$clean_url .= $url_parts['path'];
}
$user_id = 'u_' . hash( 'crc32b', $clean_url );
$common_data = [
'userId' => $user_id,
'apiHost' => 'https://app.formbricks.com',
'attributes' => [
'language' => $lang_code,
],
];
if (
isset( $reference_data['attributes'], $reference_data['attributes']['install_days_number'] )
&& is_int( $reference_data['attributes']['install_days_number'] )
) {
$common_data['attributes']['days_since_install'] = $this->install_time_category( $reference_data['attributes']['install_days_number'] );
}
return $common_data;
}
/**
* Compute the install time category.
*
* @param int $install_days_number The number of days passed since installation.
*
* @return int The category.
*/
private function install_time_category( $install_days_number ) {
if ( 1 < $install_days_number && 8 > $install_days_number ) {
return 7;
}
if ( 8 <= $install_days_number && 31 > $install_days_number ) {
return 30;
}
if ( 30 < $install_days_number && 90 > $install_days_number ) {
return 90;
}
if ( 90 <= $install_days_number ) {
return 91;
}
return 0;
}
/**
@ -142,4 +236,47 @@ class Script_Loader extends Abstract_Module {
true
);
}
/**
* Load the banner script.
*
* @param string $handler The script handler.
*
* @return void
*/
public function load_banner( $handler ) {
global $themeisle_sdk_max_path;
$asset_file = require $themeisle_sdk_max_path . '/assets/js/build/banner/banner.asset.php';
wp_enqueue_script(
$handler,
$this->get_sdk_uri() . 'assets/js/build/banner/banner.js',
$asset_file['dependencies'],
$asset_file['version'],
true
);
wp_enqueue_style(
$handler . '_style',
$this->get_sdk_uri() . 'assets/css/banner.css',
[],
$asset_file['version']
);
}
/**
* Mask a secret with `*` for half of its length.
*
* @param mixed $secret The secret.
*
* @return mixed The masked secret if secret is a valid string.
*/
public function secret_masking( $secret ) {
if ( empty( $secret ) || ! is_string( $secret ) ) {
return $secret;
}
$half_len = intval( strlen( $secret ) / 2 );
return str_repeat( '*', $half_len ) . substr( $secret, $half_len );
}
}

View File

@ -0,0 +1,247 @@
<?php
/**
* The translations model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2024, Bogdan Preda
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 3.3.23
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Translations module for ThemeIsle SDK.
*/
class Translations extends Abstract_Module {
const API_URL = 'https://translations.themeisle.com/wp-json/gpb-themeisle/';
const CACHE_KEY = 'ti_translations_data';
/**
* Check if we should load module for this.
*
* @param Product $product Product to check.
*
* @return bool Should load ?
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( $product->is_wordpress_available() ) {
return false;
}
return apply_filters( $product->get_slug() . '_sdk_enable_private_translations', false );
}
/**
* Load module logic.
*
* @param Product $product Product to load.
*
* @return Translations Module instance.
*/
public function load( $product ) {
$this->product = $product;
if ( $this->product->is_plugin() ) {
add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'add_plugin_translations' ], 11 );
} else {
add_filter( 'pre_set_site_transient_update_themes', [ $this, 'add_theme_translations' ], 11 );
}
add_filter( 'http_request_host_is_external', [ $this, 'allow_translations_api' ], 10, 3 );
return $this;
}
/**
* Allow external downloads for the translations API.
*
* @param bool $external Whether the host is external.
* @param string $host The host being checked.
* @param string $url The URL being checked.
*
* @return bool
*/
public function allow_translations_api( $external, $host, $url ) {
return strpos( $url, self::API_URL ) === 0 ? true : $external;
}
/**
* Get translations from API.
*
* @return bool | array
*/
private function get_api_translations() {
$translation_data = $this->get_translation_data();
return empty( $translation_data ) ? false : $translation_data;
}
/**
* Get translation data from API.
*
* @return array
*/
private function get_translation_data() {
$cached = get_transient( self::CACHE_KEY );
if ( $cached ) {
return $cached;
}
$response = $this->safe_get(
self::API_URL . 'translations',
array(
'timeout' => 15, //phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout, Inherited by wp_remote_get only, for vip environment we use defaults.
'sslverify' => false,
)
);
if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
return [];
}
$data = json_decode( wp_remote_retrieve_body( $response ) );
if ( ! is_array( $data ) ) {
return [];
}
set_transient( self::CACHE_KEY, $data, 12 * HOUR_IN_SECONDS );
return $data;
}
/**
* Add translations to the transient data.
*
* @param array $_transient_data incoming transient data.
* @param string $type plugins or themes.
*
* @return array
*/
public function add_translations( $_transient_data, $type = 'plugins' ) {
$translations = $this->get_api_translations();
if ( ! is_array( $translations ) ) {
return $_transient_data;
}
if ( ! isset( $_transient_data->translations ) ) {
return $_transient_data;
}
if ( ! in_array( $type, [ 'plugins', 'themes' ] ) ) {
return $_transient_data;
}
$installed_translations = wp_get_installed_translations( $type );
foreach ( $translations as $translation ) {
$translation = (array) $translation;
if ( ! $this->is_valid_translation( $translation, $type ) ) {
continue;
}
$latest_translation = strtotime( $translation['updated'] );
if ( ! is_int( $latest_translation ) ) {
continue;
}
$existing = (int) get_option( $this->get_translation_option_key( $translation ) );
$has_translation = isset( $installed_translations[ $translation['slug'] ][ $translation['language'] ] );
// If we already have the latest translation, skip.
if ( $existing >= $latest_translation && $has_translation ) {
continue;
}
$_transient_data->translations[] = $translation;
update_option( $this->get_translation_option_key( $translation ), $latest_translation );
}
return $_transient_data;
}
/**
* Add theme translations to the transient.
*
* @param array $_transient_data incoming transient data.
*
* @return array
*/
public function add_theme_translations( $_transient_data ) {
return $this->add_translations( $_transient_data, 'themes' );
}
/**
* Add plugin translations to the transient.
*
* @param array $_transient_data The transient data.
*
* @return array
*/
public function add_plugin_translations( $_transient_data ) {
return $this->add_translations( $_transient_data );
}
/**
* Get the option key for storing translations.
*
* @param array $translation the translation data from the API.
*
* @return string
*/
private function get_translation_option_key( $translation ) {
return $translation['slug'] . '_translation_' . $translation['language'];
}
/**
* Check if a translation is valid and applies for the current site.
*
* @param array $translation The translation data.
*
* @return bool
*/
private function is_valid_translation( $translation, $type = 'plugins' ) {
if ( ! isset( $translation['slug'] ) || $translation['slug'] !== $this->product->get_slug() ) {
return false;
}
$locales = apply_filters( $type . '_update_check_locales', array_values( get_available_languages() ) );
if ( ! is_array( $locales ) ) {
return false;
}
$locales = array_unique( $locales );
if ( ! isset( $translation['language'], $translation['updated'] ) ) {
return false;
}
if ( ! in_array( $translation['language'], $locales, true ) ) {
return false;
}
return true;
}
}

View File

@ -346,7 +346,17 @@ class Product {
* @return string Friendly name.
*/
public function get_friendly_name() {
$name = apply_filters( $this->get_key() . '_friendly_name', trim( str_replace( 'Lite', '', $this->get_name() ) ) );
$name = trim( str_replace( 'Lite', '', $this->get_name() ) );
if ( defined( 'OTTER_BLOCKS_BASEFILE' ) && OTTER_BLOCKS_BASEFILE === $this->basefile ) {
$name = 'Otter Blocks';
}
if ( defined( 'OPTML_BASEFILE' ) && OPTML_BASEFILE === $this->basefile ) {
$name = 'Optimole';
}
if ( defined( 'WPMM_FILE' ) && WPMM_FILE === $this->basefile ) {
$name = 'LightStart';
}
$name = apply_filters( $this->get_key() . '_friendly_name', $name );
$name = rtrim( $name, '- ()' );
return $name;