Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/post-smtp/Postman/Dashboard/NewDashboard.php
+++ b/post-smtp/Postman/Dashboard/NewDashboard.php
@@ -11,7 +11,15 @@
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
add_filter( 'post_smtp__new_dashboard', '__return_true' );
add_action( 'post_smtp__new_dashboard_content', array( $this, 'dashboard_content' ) );
- add_filter( 'post_smtp_dashboard_opened_emails_count', array( $this, 'opened_email_count' ), 10, 2 );
+
+ if (
+ is_plugin_active( 'report-and-tracking-addon-premium/post-smtp-report-and-tracking.php' )
+ ||
+ is_plugin_active( 'post-smtp-pro/post-smtp-pro.php' )
+ ) {
+ add_filter( 'post_smtp_dashboard_opened_emails_count', array( $this, 'opened_email_count' ), 10, 2 );
+ }
+
}
private function include() {
@@ -19,22 +27,24 @@
}
public function admin_enqueue_scripts( $hook ) {
- wp_enqueue_script( 'post-smtp-dashboard', POST_SMTP_URL . '/Postman/Dashboard/assets/js/app.js', array( 'wp-i18n' ), POST_SMTP_VER, true );
- wp_localize_script(
- 'post-smtp-dashboard',
- 'postSmtpNewDashboard',
- array(
- 'plugin_dir_url' => plugin_dir_url( __FILE__ ),
- 'json_url' => rest_url( 'psd/v1' ),
- 'nonce' => wp_create_nonce( 'wp_rest' ),
- 'admin_url' => admin_url( 'admin.php' ),
- 'page_hook' => $hook,
- 'is_bfcm' => postman_is_bfcm()
- )
- );
-
- wp_enqueue_style('post-smtp-dashboard', POST_SMTP_URL . '/Postman/Dashboard/assets/css/app.css', array(), POST_SMTP_VER, 'all' );
- wp_enqueue_style( 'post-smtp-dashboard-responsive', POST_SMTP_URL. '/Postman/Dashboard/assets/css/responsive-style.css', array(), POST_SMTP_VER, 'all' );
+ if ( 'toplevel_page_postman' === $hook ) {
+ wp_enqueue_script( 'post-smtp-dashboard', POST_SMTP_URL . '/Postman/Dashboard/assets/js/app.js', array( 'wp-i18n' ), POST_SMTP_VER, true );
+ wp_localize_script(
+ 'post-smtp-dashboard',
+ 'postSmtpNewDashboard',
+ array(
+ 'plugin_dir_url' => plugin_dir_url( __FILE__ ),
+ 'json_url' => rest_url( 'psd/v1' ),
+ 'nonce' => wp_create_nonce( 'wp_rest' ),
+ 'admin_url' => admin_url( 'admin.php' ),
+ 'page_hook' => $hook,
+ 'is_bfcm' => postman_is_bfcm()
+ )
+ );
+
+ wp_enqueue_style( 'post-smtp-dashboard', POST_SMTP_URL . '/Postman/Dashboard/assets/css/app.css', array(), POST_SMTP_VER, 'all' );
+ wp_enqueue_style( 'post-smtp-dashboard-responsive', POST_SMTP_URL . '/Postman/Dashboard/assets/css/responsive-style.css', array(), POST_SMTP_VER, 'all' );
+ }
}
public function dashboard_content() {
--- a/post-smtp/Postman/Extensions/Core/Notifications/PostmanNotify.php
+++ b/post-smtp/Postman/Extensions/Core/Notifications/PostmanNotify.php
@@ -84,10 +84,15 @@
//Webhook Alerts
$webhook_urls = array();
- foreach ( $_POST['postman_options']['webhook_alerts_urls'] as $key => $url ) {
- if( ! empty( $url ) ) {
- $webhook_urls[] = esc_url( $url );
+ if( isset( $_POST['postman_options']['webhook_alerts_urls'] ) ) {
+
+ foreach ( $_POST['postman_options']['webhook_alerts_urls'] as $key => $url ) {
+
+ if( ! empty( $url ) ) {
+ $webhook_urls[] = esc_url( $url );
+ }
+
}
}
--- a/post-smtp/Postman/Postman-Email-Health-Report/PostmanEmailReportSending.php
+++ b/post-smtp/Postman/Postman-Email-Health-Report/PostmanEmailReportSending.php
@@ -36,53 +36,93 @@
* @version 1.0.0
*/
public function __construct() {
-
- add_action( 'init', array( $this, 'send_report' ) );
+ add_action( 'init', array( $this, 'schedule_email_reporting' ) );
+ add_action( 'postman_rat_email_report', array( $this, 'handle_email_reporting' ) );
+ add_filter( 'cron_schedules', array( $this, 'add_monthly_schedule' ) );
}
+
/**
- * Send the report
+ * Schedules the email reporting cron event based on user-defined settings.
*
- * @since 2.9.0
- * @version 1.0.0
+ * This function retrieves the reporting interval from plugin options and schedules
+ * a WordPress cron job accordingly. If a schedule already exists and its interval
+ * is different from the new one, the existing schedule is unscheduled and a new
+ * schedule is created.
+ * @since 3.0.1
+ * @version 3.0.1
*/
- public function send_report() {
-
+ public function schedule_email_reporting() {
$options = get_option( 'postman_rat' );
+ if ( $options && isset( $options['enable_email_reporting'] ) && $options['enable_email_reporting'] ) {
+ $interval = isset( $options['reporting_interval'] ) ? $options['reporting_interval'] : false;
- $enabled = ( $options && isset( $options['enable_email_reporting'] ) ) ? $options['enable_email_reporting'] : false;
-
- $interval = ( $options && isset( $options['reporting_interval'] ) ) ? $options['reporting_interval'] : false;
-
- $has_sent = get_transient( 'ps_rat_has_sent' );
-
- // If transient expired, let's send :).
- if ( $enabled && $interval && ! $has_sent ) {
-
- $expiry_time = '';
- $report_sent = $this->send_mail( $interval );
-
- if ( $report_sent ) {
-
- if ( $interval === 'd' ) {
-
- $expiry_time = DAY_IN_SECONDS;
- }
- if ( $interval === 'w' ) {
-
- $expiry_time = WEEK_IN_SECONDS;
+ if ( $interval ) {
+ $schedules = array(
+ 'd' => 'daily',
+ 'w' => 'weekly',
+ 'm' => 'monthly',
+ );
+
+ $schedule = isset( $schedules[ $interval ] ) ? $schedules[ $interval ] : false;
+ if ( $schedule ) {
+ $timestamp = wp_next_scheduled( 'postman_rat_email_report' );
+ if ( $timestamp ) {
+ $current_interval = wp_get_schedule( 'postman_rat_email_report' );
+ if ( $current_interval !== $schedule ) {
+ wp_unschedule_event( $timestamp, 'postman_rat_email_report' );
+ } else {
+ return;
+ }
+ }
+ $current_time = current_time( 'timestamp' );
+ $midnight = strtotime( 'tomorrow midnight', $current_time ) - 1;
+ wp_schedule_event( $current_time, $schedule, 'postman_rat_email_report' );
}
- if ( $interval === 'm' ) {
+ }
+ }else{
+ $interval = isset( $options['reporting_interval'] ) ? $options['reporting_interval'] : false;
+ $timestamp = wp_next_scheduled( 'postman_rat_email_report' );
+ if ( $timestamp ) {
+ wp_unschedule_event( $timestamp, 'postman_rat_email_report' );
+ }
+ }
+ }
- $expiry_time = MONTH_IN_SECONDS;
- }
+ /**
+ * Handles the email reporting functionality triggered by the cron job.
+ *
+ * This function checks if email reporting is enabled and retrieves the configured
+ * reporting interval. If both conditions are met, it triggers the email-sending
+ * functionality.
+ * @since 3.0.1
+ * @version 3.0.1
+ */
+ public function handle_email_reporting() {
+ $options = get_option( 'postman_rat' );
+ $enabled = isset( $options['enable_email_reporting'] ) ? $options['enable_email_reporting'] : false;
+ $interval = isset( $options['reporting_interval'] ) ? $options['reporting_interval'] : false;
- // Set Future Transient :D.
- set_transient( 'ps_rat_has_sent', '1', $expiry_time );
- }
+ if ( $enabled && $interval ) {
+ $report_sent = $this->send_mail( $interval );
}
}
+ /**
+ * Add a custom monthly schedule to WordPress's cron system.
+ *
+ * @param array $schedules The existing cron schedules.
+ * @return array Modified array of cron schedules with 'monthly' added.
+ * @since 3.0.1
+ * @version 3.0.1
+ */
+ public function add_monthly_schedule( $schedules ) {
+ $schedules['monthly'] = array(
+ 'interval' => 30 * DAY_IN_SECONDS,
+ 'display' => __( 'Once Monthly', 'post-smtp' ),
+ );
+ return $schedules;
+ }
/**
* Get total email count
--- a/post-smtp/Postman/Postman-Email-Log/PostmanEmailLogService.php
+++ b/post-smtp/Postman/Postman-Email-Log/PostmanEmailLogService.php
@@ -161,15 +161,15 @@
$data['solution'] = apply_filters( 'post_smtp_log_solution', null, $new_status, $log, $message );
$data['success'] = empty( $new_status ) ? 1 : $new_status;
$data['from_header'] = $log->sender;
- $data['to_header'] = !empty( $log->toRecipients ) ? $log->toRecipients : '';
- $data['cc_header'] = !empty( $log->ccRecipients ) ? $log->ccRecipients : '';
- $data['bcc_header'] = !empty( $log->bccRecipients ) ? $log->bccRecipients : '';
- $data['reply_to_header'] = !empty( $log->replyTo ) ? $log->replyTo : '';
- $data['transport_uri'] = !empty( $log->transportUri ) ? $log->transportUri : '';
- $data['original_to'] = is_array( $log->originalTo ) ? implode( ',', $log->originalTo ) : $log->originalTo;
- $data['original_subject'] = !empty( $log->originalSubject ) ? $log->originalSubject : '';
- $data['original_message'] = $log->originalMessage;
- $data['original_headers'] = is_array($log->originalHeaders) ? serialize($log->originalHeaders) : $log->originalHeaders;
+ $data['to_header'] = !empty( $log->toRecipients ) ? implode( ',', array_map( 'sanitize_email', explode( ',', $log->toRecipients ) ) ) : '';
+ $data['cc_header'] = !empty( $log->ccRecipients ) ? implode( ',', array_map( 'sanitize_email', explode( ',', $log->ccRecipients ) ) ) : '';
+ $data['bcc_header'] = !empty( $log->bccRecipients ) ? implode( ',', array_map( 'sanitize_email', explode( ',', $log->bccRecipients ) ) ) : '';
+ $data['reply_to_header'] = !empty( $log->replyTo ) ? implode( ',', array_map( 'sanitize_email', explode( ',', $log->replyTo ) ) ) : '';
+ $data['transport_uri'] = !empty( $log->transportUri ) ? $log->transportUri : '';
+ $data['original_to'] = is_array( $log->originalTo ) ? implode( ',', array_map( 'sanitize_email', explode( ',', $log->originalTo ) ) ) : '';
+ $data['original_subject'] = !empty( $log->originalSubject ) ? sanitize_text_field( $log->originalSubject ) : '';
+ $data['original_message'] = !empty( $log->originalMessage ) ? sanitize_textarea_field( $log->originalMessage ) : '';
+ $data['original_headers'] = is_array($log->originalHeaders) ? serialize( $log->originalHeaders ) : $log->originalHeaders;
$data['session_transcript'] = $log->sessionTranscript;
$email_logs = new PostmanEmailLogs();
@@ -182,9 +182,8 @@
* @version 1.0.0
*/
$log_id = apply_filters( 'post_smtp_update_email_log_id', '' );
-
$log_id = $email_logs->save( $data, $log_id );
-
+
/**
* Fires after the email log is saved
*
--- a/post-smtp/Postman/Postman-Email-Log/PostmanEmailQueryLog.php
+++ b/post-smtp/Postman/Postman-Email-Log/PostmanEmailQueryLog.php
@@ -141,24 +141,25 @@
// Status Filter
$clause_for_status = '';
- if( !empty( $args['status'] ) ) {
+ if( isset( $args['status'] ) && !empty( $args['status'] ) ) {
$clause_for_status = strpos( $this->query, 'WHERE' ) !== FALSE ? ' AND' : ' WHERE';
+
+ if( $args['status'] == 'success' ) {
- }
- if( $args['status'] == 'success' ) {
-
- $this->query .= "{$clause_for_status} `success` = 1 ";
-
- }
- elseif ( $args['status'] == 'failed' ) {
-
- $this->query .= "{$clause_for_status} `success` != 1 ";
-
- }
- else {
-
- $this->query .= '';
+ $this->query .= "{$clause_for_status} `success` = 1 ";
+
+ }
+ elseif ( $args['status'] == 'failed' ) {
+
+ $this->query .= "{$clause_for_status} `success` != 1 ";
+
+ }
+ else {
+
+ $this->query .= '';
+
+ }
}
--- a/post-smtp/Postman/Postman-Mail/PostmanSendGridTransport.php
+++ b/post-smtp/Postman/Postman-Mail/PostmanSendGridTransport.php
@@ -189,6 +189,15 @@
$this,
'sendgrid_api_key_callback'
), PostmanSendGridTransport::SENDGRID_AUTH_OPTIONS, PostmanSendGridTransport::SENDGRID_AUTH_SECTION );
+
+ add_settings_field(
+ PostmanOptions::SENDGRID_REGION,
+ __( 'Region', 'post-smtp' ),
+ array( $this, 'sendgrid_region_callback' ),
+ PostmanSendGridTransport::SENDGRID_AUTH_OPTIONS,
+ PostmanSendGridTransport::SENDGRID_AUTH_SECTION
+ );
+
}
public function printSendGridAuthSectionInfo() {
/* Translators: Where (1) is the service URL and (2) is the service name and (3) is a api key URL */
@@ -201,6 +210,24 @@
printf ( '<input type="password" autocomplete="off" id="sendgrid_api_key" name="postman_options[sendgrid_api_key]" value="%s" size="60" class="required ps-input ps-w-75" placeholder="%s"/>', null !== $this->options->getSendGridApiKey () ? esc_attr ( PostmanUtils::obfuscatePassword ( $this->options->getSendGridApiKey () ) ) : '', __ ( 'Required', 'post-smtp' ) );
print ' <input type="button" id="toggleSendGridApiKey" value="Show Password" class="button button-secondary" style="visibility:hidden" />';
}
+
+ /**
+ * Renders the SendGrid region selection dropdown.
+ *
+ * Outputs a select box for users to choose the SendGrid region (Global or EU).
+ *
+ * @since 3.1.0
+ * @version 1.0.0
+ */
+ public function sendgrid_region_callback() {
+ $options = get_option( PostmanOptions::POSTMAN_OPTIONS );
+ $selected_region = isset( $options[ PostmanOptions::SENDGRID_REGION ] ) ? $options[ PostmanOptions::SENDGRID_REGION ] : 'Global';
+
+ echo '<select name="postman_options[' . esc_attr( PostmanOptions::SENDGRID_REGION ) . ']">';
+ echo '<option value="Global"' . selected( $selected_region, 'Global', false ) . '>' . __( 'Global', 'post-smtp' ) . '</option>';
+ echo '<option value="EU"' . selected( $selected_region, 'EU', false ) . '>' . __( 'Europe (EU)', 'post-smtp' ) . '</option>';
+ echo '</select>';
+ }
/**
*/
--- a/post-smtp/Postman/Postman-Mail/Services/SendGrid/Handler.php
+++ b/post-smtp/Postman/Postman-Mail/Services/SendGrid/Handler.php
@@ -26,6 +26,13 @@
*/
private $base_url = 'https://api.sendgrid.com/v3/mail';
+ /**
+ * Options instance
+ *
+ * @var PostmanOptions
+ */
+ private $options;
+
/**
* constructor PostmanSendGrid
*
@@ -36,13 +43,14 @@
public function __construct( $api_key ) {
$this->api_key = $api_key;
+ $this->options = PostmanOptions::getInstance();
+ $region = $this->options->getSendGridRegion();
- if( apply_filters( 'post_smtp_enable_sendgrid_eu', false ) ) {
-
+ if ( 'EU' === $region || apply_filters( 'post_smtp_enable_sendgrid_eu', false ) ) {
$this->base_url = 'https://api.eu.sendgrid.com/v3/mail';
-
}
+ $this->base_url = apply_filters( 'post_smtp_sendgrid_base_url', $this->base_url, $region );
parent::__construct( $this->base_url );
}
--- a/post-smtp/Postman/Postman.php
+++ b/post-smtp/Postman/Postman.php
@@ -172,10 +172,10 @@
) );
// hook on the plugins_loaded event
- add_action( 'plugins_loaded', array(
- $this,
- 'on_plugins_loaded',
- ) );
+ add_action( 'init', array(
+ $this,
+ 'on_init',
+ ), 0 );
//Conflicting with backupbuddy, will be removed soon
//add_filter( 'extra_plugin_headers', [ $this, 'add_extension_headers' ] );
@@ -213,14 +213,11 @@
* "After active plugins and pluggable functions are loaded"
* ref: http://codex.wordpress.org/Plugin_API/Action_Reference#Actions_Run_During_a_Typical_Request
*/
- public function on_plugins_loaded() {
+ public function on_init() {
// register the email transports
$this->registerTransports( $this->rootPluginFilenameAndPath );
- // load the text domain
- $this->loadTextDomain();
-
// register the setup_admin function on plugins_loaded because we need to call
// current_user_can to verify the capability of the current user
if ( PostmanUtils::isAdmin() && is_admin() ) {
@@ -494,26 +491,6 @@
}
/**
- * Loads the appropriate language file
- */
- private function loadTextDomain() {
- // had to hardcode the third parameter, Relative path to WP_PLUGIN_DIR,
- // because __FILE__ returns the wrong path if the plugin is installed as a symlink
- $shortLocale = substr( get_locale(), 0, 2 );
- if ( $shortLocale != 'en' ) {
- $langDir = 'post-smtp/Postman/languages';
- $success = load_plugin_textdomain( 'post-smtp', false, $langDir );
- if ( $this->logger->isDebug() ) {
- if ( $success ) {
- $this->logger->debug( sprintf( 'local translation file loaded for locale=%s', get_locale() ) );
- } else {
- $this->logger->debug( sprintf( 'failed to load local translation file: locale=%s file=%s/%s-%s.mo', get_locale(), $langDir, 'post-smtp', get_locale() ) );
- }
- }
- }
- }
-
- /**
* Shortcode to return the current plugin version.
*
* From http://code.garyjones.co.uk/get-wordpress-plugin-version/
--- a/post-smtp/Postman/PostmanEmailLogs.php
+++ b/post-smtp/Postman/PostmanEmailLogs.php
@@ -264,12 +264,9 @@
}
else {
-
- return $this->db->insert(
- $this->db->prefix . $this->db_name,
- $data
- ) ? $this->db->insert_id : false;
-
+ if( isset( $data[ 'success' ] ) ) {
+ return $this->db->insert( $this->db->prefix . $this->db_name, $data ) ? $this->db->insert_id : false;
+ }
}
}
--- a/post-smtp/Postman/PostmanInputSanitizer.php
+++ b/post-smtp/Postman/PostmanInputSanitizer.php
@@ -54,6 +54,7 @@
$this->sanitizeString( 'Client ID', PostmanOptions::CLIENT_ID, $input, $new_input );
$this->sanitizeString( 'Client Secret', PostmanOptions::CLIENT_SECRET, $input, $new_input );
$this->sanitizeString( 'Username', PostmanOptions::BASIC_AUTH_USERNAME, $input, $new_input );
+ $this->sanitizeString( 'SendGrid Region', PostmanOptions::SENDGRID_REGION, $input, $new_input, $this->options->getSendGridRegion() );
$this->sanitizePassword( 'Password', PostmanOptions::BASIC_AUTH_PASSWORD, $input, $new_input, $this->options->getPassword() );
$this->sanitizePassword( 'Mandrill API Key', PostmanOptions::MANDRILL_API_KEY, $input, $new_input, $this->options->getMandrillApiKey() );
$this->sanitizePassword( 'SendGrid API Key', PostmanOptions::SENDGRID_API_KEY, $input, $new_input, $this->options->getSendGridApiKey() );
--- a/post-smtp/Postman/PostmanOptions.php
+++ b/post-smtp/Postman/PostmanOptions.php
@@ -89,6 +89,7 @@
const BASIC_AUTH_PASSWORD = 'basic_auth_password';
const MANDRILL_API_KEY = 'mandrill_api_key';
const SENDGRID_API_KEY = 'sendgrid_api_key';
+ const SENDGRID_REGION = 'sendgrid_region';
const SENDINBLUE_API_KEY = 'sendinblue_api_key';
const MAILJET_API_KEY = 'mailjet_api_key';
const MAILJET_SECRET_KEY = 'mailjet_secret_key';
@@ -483,6 +484,28 @@
if ( isset( $this->options [ PostmanOptions::SENDGRID_API_KEY ] ) ) {
return base64_decode( $this->options [ PostmanOptions::SENDGRID_API_KEY ] ); }
}
+
+ /**
+ * Retrieves the configured SendGrid region.
+ *
+ * Checks if the `POST_SMTP_API_KEY` is defined or returns the region from options.
+ *
+ * @since 3.1.0
+ * @version 1.0.0
+ *
+ * @return string|null The SendGrid region or null if not set.
+ */
+ public function getSendGridRegion() {
+ if ( defined( 'POST_SMTP_API_KEY' ) ) {
+ return POST_SMTP_API_KEY;
+ }
+
+ if ( isset( $this->options [ PostmanOptions::SENDGRID_REGION ] ) ) {
+ return esc_attr( $this->options[ PostmanOptions::SENDGRID_REGION ] );
+ }
+
+ return null; // Default to null if no region is set.
+ }
public function getMailgunApiKey() {
if ( defined( 'POST_SMTP_API_KEY' ) ) {
return POST_SMTP_API_KEY;
@@ -664,12 +687,12 @@
/**
* (non-PHPdoc)
- *
- * @see PostmanOptions::isSenderNameOverridePrevented()
+ *
* @deprecated by isPluginSenderNameEnforced
+ *
*/
public function isSenderNameOverridePrevented() {
- return $this->isPluginSenderEmailEnforced();
+ return $this->isPluginSenderNameEnforced();
}
public function isPluginSenderEmailEnforced() {
--- a/post-smtp/Postman/Wizard/NewWizard.php
+++ b/post-smtp/Postman/Wizard/NewWizard.php
@@ -178,17 +178,17 @@
<table>
<tr class="<?php echo esc_attr( $in_active ) ?>">
<td class="ps-wizard-circle"><span class="ps-tick dashicons dashicons-yes-alt"></span></td>
- <td class="ps-wizard-text">Choose your SMTP Mailer</td>
+ <td class="ps-wizard-text"><?php _e( 'Choose your SMTP Mailer', 'post-smtp' ) ?></td>
<td class="ps-wizard-edit"><span class="dashicons dashicons-edit" data-step="1"></span></td>
</tr>
<tr class="<?php echo esc_attr( $is_active ) ?>">
<td class="ps-wizard-circle"><span class="ps-tick dashicons dashicons-yes-alt"><span class="ps-wizard-line"></span></span></td>
- <td class="ps-wizard-text">Configure Mailer Settings</td>
+ <td class="ps-wizard-text"><?php _e( 'Configure Mailer Settings', 'post-smtp' ) ?></td>
<td class="ps-wizard-edit"><span class="dashicons dashicons-edit" data-step="2"></span></td>
</tr>
<tr class="ps-in-active-nav">
<td class="ps-wizard-circle"><span class="ps-tick dashicons dashicons-yes-alt"><span class="ps-wizard-line"></span></span></td>
- <td class="ps-wizard-text">Send Test Email</td>
+ <td class="ps-wizard-text"><?php _e( 'Send Test Email', 'post-smtp' ) ?></td>
<td class="ps-wizard-edit"><span class="dashicons dashicons-edit" data-step="3"></span></td>
</tr>
</table>
@@ -371,16 +371,16 @@
?>
</div>
<div class="ps-wizard-step ps-wizard-step-3">
- <a href="" data-step="2" class="ps-wizard-back"><span class="dashicons dashicons-arrow-left-alt"></span>Back</a>
+ <a href="" data-step="2" class="ps-wizard-back"><span class="dashicons dashicons-arrow-left-alt"></span><?php _e( 'Back', 'post-smtp' ) ?></a>
<p><?php _e( 'This step allows you to send an email message for testing. If there is a problem, Post SMTP will give up after 60 seconds.', 'post-smtp' ); ?></p>
<div class="ps-form-ui">
<div class="ps-form-control">
- <div><label>Recipient Email Address</label></div>
+ <div><label><?php _e( 'Recipient Email Address', 'post-smtp' ) ?></label></div>
<input type="text" class="ps-test-to" required data-error="Enter Recipient Email Address" name="postman_test_options[test_email]" value="<?php echo esc_attr( wp_get_current_user()->user_email ); ?>" placeholder="Recipient Email Address">
- <span class="ps-form-control-info">Enter the email address where you want to send a test email message.</span>
- <p class="ps-form-control-info">Are your WordPress emails getting broken? Check out our guide on <a href="https://postmansmtp.com/fix-for-broken-emails/?utm_source=plugin&utm_medium=wizard&utm_campaign=plugin" target="_blank">how to Fix Broken Emails</a>.</p>
+ <span class="ps-form-control-info"><?php _e( 'Enter the email address where you want to send a test email message.', 'post-smtp' ) ?></span>
+ <p class="ps-form-control-info"><?php _e( 'Are your WordPress emails getting broken? Check out our guide on', 'post-smtp' ) ?> <a href="https://postmansmtp.com/fix-for-broken-emails/?utm_source=plugin&utm_medium=wizard&utm_campaign=plugin" target="_blank"><?php _e( 'how to Fix Broken Emails', 'post-smtp' ) ?></a>.</p>
</div>
- <button class="button button-primary ps-blue-btn ps-wizard-send-test-email" data-step="3">Send Test Email <span class="dashicons dashicons-email"></span></button>
+ <button class="button button-primary ps-blue-btn ps-wizard-send-test-email" data-step="3"><?php _e( 'Send Test Email', 'post-smtp' ) ?> <span class="dashicons dashicons-email"></span></button>
<div>
<p class="ps-wizard-error"></p>
<p class="ps-wizard-success"></p>
@@ -395,7 +395,7 @@
*/
printf(
'%1$s <a href="%2$s" target="_blank">%3$s</a>',
- __( 'We value your opinion on your experience with Post SMTP and would appreciate your feedback. ' ),
+ __( 'We value your opinion on your experience with Post SMTP and would appreciate your feedback. ', 'post-smtp' ),
esc_url( 'https://wordpress.org/support/plugin/post-smtp/reviews/#new-post' ),
__( 'Leave a review here.', 'post-smtp' )
) ?></p>
@@ -405,23 +405,23 @@
<div class="ps-mobile-notice-features">
<div class="ps-mobile-feature-left">
<span class="dashicons dashicons-yes-alt"></span>
- Easy Email Tracking
+ <?php _e( 'Easy Email Tracking', 'post-smtp' ) ?>
<br>
<span class="dashicons dashicons-yes-alt"></span>
- Quickly View Error Details
+ <?php _e( 'Quickly View Error Details', 'post-smtp' ) ?>
<br>
<span class="dashicons dashicons-yes-alt"></span>
- Easy Email Tracking
+ <?php _e( 'Easy Email Tracking', 'post-smtp' ) ?>
</div>
<div class="ps-mobile-feature-right">
<span class="dashicons dashicons-yes-alt"></span>
- Get Email Preview
+ <?php _e( 'Get Email Preview', 'post-smtp' ) ?>
<br>
<span class="dashicons dashicons-yes-alt"></span>
- Resend Failed Emails
+ <?php _e( 'Resend Failed Emails', 'post-smtp' ) ?>
<br>
<span class="dashicons dashicons-yes-alt"></span>
- Support multiple sites
+ <?php _e( 'Support multiple sites', 'post-smtp' ) ?>
</div>
</div>
<div style="display: flex; margin-top: 15px;">
@@ -456,13 +456,13 @@
<div class="ps-wizard-footer-right">
<div class="ps-wizard-step ps-wizard-step-1">
<p class="ps-wizard-error"></p>
- <button class="button button-primary ps-blue-btn ps-wizard-next-btn" data-step="1">Continue <span class="dashicons dashicons-arrow-right-alt"></span></button>
+ <button class="button button-primary ps-blue-btn ps-wizard-next-btn" data-step="1"><?php _e( 'Continue', 'post-smtp' ) ?> <span class="dashicons dashicons-arrow-right-alt"></span></button>
<div style="clear: both"></div>
</div>
<div class="ps-wizard-step ps-wizard-step-2">
<p class="ps-wizard-success"><?php echo ( isset( $_GET['success'] ) && isset( $_GET['msg'] ) ) ? sanitize_text_field( $_GET['msg'] ) : ''; ?></p>
<p class="ps-wizard-error"><?php echo ( !isset( $_GET['success'] ) && isset( $_GET['msg'] ) ) ? sanitize_text_field( $_GET['msg'] ) : ''; ?></p>
- <button class="button button-primary ps-blue-btn ps-wizard-next-btn" data-step="2"></span>Save and Continue <span class="dashicons dashicons-arrow-right-alt"></span></button>
+ <button class="button button-primary ps-blue-btn ps-wizard-next-btn" data-step="2"></span><?php _e( 'Save and Continue', 'post-smtp' ) ?> <span class="dashicons dashicons-arrow-right-alt"></span></button>
<div style="clear: both"></div>
</div>
<div class="ps-wizard-step ps-wizard-step-3">
@@ -602,7 +602,7 @@
<div class="ps-form-control">
<div><label>From Name</label></div>
<input type="text" class="ps-from-name" required data-error="'.__( 'Please enter From Name.', 'post-smtp' ).'" name="postman_options['.esc_attr( PostmanOptions::MESSAGE_SENDER_NAME ).']" value="'.$from_name.'" placeholder="From Name">
- <span class="ps-form-control-info">The name that emails are sent from.</span>
+ <span class="ps-form-control-info">'.__( 'The name that emails are sent from.', 'post-smtp' ).'</span>
<div>
<div class="ps-form-switch-control">
<label class="ps-switch-1">
@@ -905,7 +905,7 @@
public function render_sendgrid_settings() {
$api_key = null !== $this->options->getSendGridApiKey() ? esc_attr ( $this->options->getSendGridApiKey() ) : '';
-
+ $selected_region = $this->options->getSendGridRegion() ? esc_attr( $this->options->getSendGridRegion() ) : 'AG';
$html = sprintf(
'<p><a href="%1$s" target="_blank">%2$s</a> %3$s</p><p>%4$s <a href="%5$s" target="_blank">%6$s</a></p>',
esc_url( 'https://sendgrid.com/' ),
@@ -916,6 +916,8 @@
__( 'SendGrid Documentation', 'post-smtp' )
);
+
+
$html .= '
<div class="ps-form-control">
<div><label>API Key</label></div>
@@ -935,6 +937,16 @@
</div>
';
+ // Region dropdown.
+ $html .= '<div class="ps-form-control">';
+ $html .= '<div><label>' . __( 'Region', 'post-smtp' ) . '</label></div>';
+ $html .= '<select name="postman_options[' . esc_attr( PostmanOptions::SENDGRID_REGION ) . ']" class="ps-sendgrid-region">';
+ $html .= '<option value="Global" ' . selected( $selected_region, 'Global', false ) . '>' . __( 'Global', 'post-smtp' ) . '</option>';
+ $html .= '<option value="EU" ' . selected( $selected_region, 'EU', false ) . '>' . __( 'Europe (EU)', 'post-smtp' ) . '</option>';
+ $html .= '</select>';
+ $html .= '</div>';
+
+
return $html;
}
@@ -1647,6 +1659,7 @@
$sanitized['postmark_api_key'] = isset( $sanitized['postmark_api_key'] ) ? $sanitized['postmark_api_key'] : '';
$sanitized['mailgun_api_key'] = isset( $sanitized['mailgun_api_key'] ) ? $sanitized['mailgun_api_key'] : '';
$sanitized[PostmanOptions::SENDGRID_API_KEY] = isset( $sanitized[PostmanOptions::SENDGRID_API_KEY] ) ? $sanitized[PostmanOptions::SENDGRID_API_KEY] : '';
+ $sanitized['sendgrid_region'] = isset( $sanitized['sendgrid_region'] ) ? $sanitized['sendgrid_region'] : '';
$sanitized['mandrill_api_key'] = isset( $sanitized['mandrill_api_key'] ) ? $sanitized['mandrill_api_key'] : '';
$sanitized['elasticemail_api_key'] = isset( $sanitized['elasticemail_api_key'] ) ? $sanitized['elasticemail_api_key'] : '';
$sanitized[PostmanOptions::MAILJET_API_KEY] = isset( $sanitized[PostmanOptions::MAILJET_API_KEY] ) ? $sanitized[PostmanOptions::MAILJET_API_KEY] : '';
@@ -1657,7 +1670,7 @@
$sanitized['ses_region'] = isset( $sanitized['ses_region'] ) ? $sanitized['ses_region'] : '';
$sanitized['enc_type'] = 'tls';
$sanitized['auth_type'] = 'login';
-
+
foreach( $sanitized as $key => $value ) {
$options[$key] = $value;
@@ -1669,9 +1682,7 @@
$response = true;
} else {
-
- $response = update_option( PostmanOptions::POSTMAN_OPTIONS, $options );
-
+ $response = update_option( PostmanOptions::POSTMAN_OPTIONS , $options );
}
}
--- a/post-smtp/freemius/includes/class-freemius.php
+++ b/post-smtp/freemius/includes/class-freemius.php
@@ -110,6 +110,12 @@
private $_enable_anonymous = true;
/**
+ * @since 2.9.1
+ * @var string|null Hints the SDK whether the plugin supports parallel activation mode, preventing the auto-deactivation of the free version when the premium version is activated, and vice versa.
+ */
+ private $_premium_plugin_basename_from_parallel_activation;
+
+ /**
* @since 1.1.7.5
* @var bool Hints the SDK if plugin should run in anonymous mode (only adds feedback form).
*/
@@ -1651,6 +1657,31 @@
);
}
}
+
+ if (
+ $this->is_user_in_admin() &&
+ $this->is_parallel_activation() &&
+ $this->_premium_plugin_basename !== $this->_premium_plugin_basename_from_parallel_activation
+ ) {
+ $this->_premium_plugin_basename = $this->_premium_plugin_basename_from_parallel_activation;
+
+ register_activation_hook(
+ dirname( $this->_plugin_dir_path ) . '/' . $this->_premium_plugin_basename,
+ array( &$this, '_activate_plugin_event_hook' )
+ );
+ }
+ }
+
+ /**
+ * Determines if a plugin is running in parallel activation mode.
+ *
+ * @author Leo Fajardo (@leorw)
+ * @since 2.9.1
+ *
+ * @return bool
+ */
+ private function is_parallel_activation() {
+ return ! empty( $this->_premium_plugin_basename_from_parallel_activation );
}
/**
@@ -5155,11 +5186,35 @@
$this->_plugin :
new FS_Plugin();
+ $is_premium = $this->get_bool_option( $plugin_info, 'is_premium', true );
$premium_suffix = $this->get_option( $plugin_info, 'premium_suffix', '(Premium)' );
+ $module_type = $this->get_option( $plugin_info, 'type', $this->_module_type );
+
+ $parallel_activation = $this->get_option( $plugin_info, 'parallel_activation' );
+
+ if (
+ ! $is_premium &&
+ is_array( $parallel_activation ) &&
+ ( WP_FS__MODULE_TYPE_PLUGIN === $module_type ) &&
+ $this->get_bool_option( $parallel_activation, 'enabled' )
+ ) {
+ $premium_basename = $this->get_option( $parallel_activation, 'premium_version_basename' );
+
+ if ( empty( $premium_basename ) ) {
+ throw new Exception('You need to specify the premium version basename to enable parallel version activation.');
+ }
+
+ $this->_premium_plugin_basename_from_parallel_activation = $premium_basename;
+
+ if ( is_plugin_active( $premium_basename ) ) {
+ $is_premium = true;
+ }
+ }
+
$plugin->update( array(
'id' => $id,
- 'type' => $this->get_option( $plugin_info, 'type', $this->_module_type ),
+ 'type' => $module_type,
'public_key' => $public_key,
'slug' => $this->_slug,
'premium_slug' => $this->get_option( $plugin_info, 'premium_slug', "{$this->_slug}-premium" ),
@@ -5167,7 +5222,7 @@
'version' => $this->get_plugin_version(),
'title' => $this->get_plugin_name( $premium_suffix ),
'file' => $this->_plugin_basename,
- 'is_premium' => $this->get_bool_option( $plugin_info, 'is_premium', true ),
+ 'is_premium' => $is_premium,
'premium_suffix' => $premium_suffix,
'is_live' => $this->get_bool_option( $plugin_info, 'is_live', true ),
'affiliate_moderation' => $this->get_option( $plugin_info, 'has_affiliation' ),
@@ -5236,7 +5291,14 @@
$this->_anonymous_mode = false;
} else {
$this->_enable_anonymous = $this->get_bool_option( $plugin_info, 'enable_anonymous', true );
- $this->_anonymous_mode = $this->get_bool_option( $plugin_info, 'anonymous_mode', false );
+ $this->_anonymous_mode = (
+ $this->get_bool_option( $plugin_info, 'anonymous_mode', false ) ||
+ (
+ $this->apply_filters( 'playground_anonymous_mode', true ) &&
+ ! empty( $_SERVER['HTTP_HOST'] ) &&
+ FS_Site::is_playground_wp_environment_by_host( $_SERVER['HTTP_HOST'] )
+ )
+ );
}
$this->_permissions = $this->get_option( $plugin_info, 'permissions', array() );
$this->_is_bundle_license_auto_activation_enabled = $this->get_option( $plugin_info, 'bundle_license_auto_activation', false );
@@ -5444,7 +5506,7 @@
if ( $this->is_registered() ) {
// Schedule code type changes event.
- $this->schedule_install_sync();
+ $this->maybe_schedule_install_sync_cron();
}
/**
@@ -6508,6 +6570,33 @@
}
/**
+ * Instead of running blocking install sync event, execute non blocking scheduled cron job.
+ *
+ * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding specified blog ID from being the cron job executor.
+ *
+ * @author Leo Fajardo (@leorw)
+ * @since 2.9.1
+ */
+ private function maybe_schedule_install_sync_cron( $except_blog_id = 0 ) {
+ if ( ! $this->is_user_in_admin() ) {
+ return;
+ }
+
+ if ( $this->is_clone() ) {
+ return;
+ }
+
+ if (
+ // The event has been properly scheduled, so no need to reschedule it.
+ is_numeric( $this->next_install_sync() )
+ ) {
+ return;
+ }
+
+ $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id );
+ }
+
+ /**
* @author Vova Feldman (@svovaf)
* @since 1.1.7.3
*
@@ -6605,22 +6694,6 @@
}
/**
- * Instead of running blocking install sync event, execute non blocking scheduled wp-cron.
- *
- * @author Vova Feldman (@svovaf)
- * @since 1.1.7.3
- *
- * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding excluded specified blog ID from being the cron executor.
- */
- private function schedule_install_sync( $except_blog_id = 0 ) {
- if ( $this->is_clone() ) {
- return;
- }
-
- $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id );
- }
-
- /**
* Unix timestamp for previous install sync cron execution or false if never executed.
*
* @todo There's some very strange bug that $this->_storage->install_sync_timestamp value is not being updated. But for sure the sync event is working.
@@ -7411,7 +7484,7 @@
*/
if (
is_plugin_active( $other_version_basename ) &&
- $this->apply_filters( 'deactivate_on_activation', true )
+ $this->apply_filters( 'deactivate_on_activation', ! $this->is_parallel_activation() )
) {
deactivate_plugins( $other_version_basename );
}
@@ -7425,7 +7498,7 @@
// Schedule re-activation event and sync.
// $this->sync_install( array(), true );
- $this->schedule_install_sync();
+ $this->maybe_schedule_install_sync_cron();
// If activating the premium module version, add an admin notice to congratulate for an upgrade completion.
if ( $is_premium_version_activation ) {
@@ -8616,7 +8689,7 @@
return;
}
- $this->schedule_install_sync();
+ $this->maybe_schedule_install_sync_cron();
// $this->sync_install( array(), true );
}
@@ -15974,7 +16047,7 @@
if ( $this->is_install_sync_scheduled() &&
$context_blog_id == $this->get_install_sync_cron_blog_id()
) {
- $this->schedule_install_sync( $context_blog_id );
+ $this->maybe_schedule_install_sync_cron( $context_blog_id );
}
}
@@ -23927,13 +24000,15 @@
// Start trial button.
$button = ' ' . sprintf(
- '<a style="margin-left: 10px; vertical-align: super;" href="%s"><button class="button button-primary">%s ➜</button></a>',
+ '<div><a class="button button-primary" href="%s">%s ➜</a></div>',
$trial_url,
$this->get_text_x_inline( 'Start free trial', 'call to action', 'start-free-trial' )
);
+ $message_text = $this->apply_filters( 'trial_promotion_message', "{$message} {$cc_string}" );
+
$this->_admin_notices->add_sticky(
- $this->apply_filters( 'trial_promotion_message', "{$message} {$cc_string} {$button}" ),
+ "<div class="fs-trial-message-container"><div>{$message_text}</div> {$button}</div>",
'trial_promotion',
'',
'promotion'
@@ -25403,7 +25478,7 @@
$img_dir = WP_FS__DIR_IMG;
// Locate the main assets folder.
- if ( 1 < count( $fs_active_plugins->plugins ) ) {
+ if ( ! empty( $fs_active_plugins->plugins ) ) {
$plugin_or_theme_img_dir = ( $this->is_plugin() ? WP_PLUGIN_DIR : get_theme_root( get_stylesheet() ) );
foreach ( $fs_active_plugins->plugins as $sdk_path => &$data ) {
--- a/post-smtp/freemius/includes/class-fs-plugin-updater.php
+++ b/post-smtp/freemius/includes/class-fs-plugin-updater.php
@@ -542,24 +542,8 @@
global $wp_current_filter;
- $current_plugin_version = $this->_fs->get_plugin_version();
-
- if ( ! empty( $wp_current_filter ) && 'upgrader_process_complete' === $wp_current_filter[0] ) {
- if (
- is_null( $this->_update_details ) ||
- ( is_object( $this->_update_details ) && $this->_update_details->new_version !== $current_plugin_version )
- ) {
- /**
- * After an update, clear the stored update details and reparse the plugin's main file in order to get
- * the updated version's information and prevent the previous update information from showing up on the
- * updates page.
- *
- * @author Leo Fajardo (@leorw)
- * @since 2.3.1
- */
- $this->_update_details = null;
- $current_plugin_version = $this->_fs->get_plugin_version( true );
- }
+ if ( ! empty( $wp_current_filter ) && in_array( 'upgrader_process_complete', $wp_current_filter ) ) {
+ return $transient_data;
}
if ( ! isset( $this->_update_details ) ) {
@@ -568,7 +552,7 @@
false,
fs_request_get_bool( 'force-check' ),
FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION,
- $current_plugin_version
+ $this->_fs->get_plugin_version()
);
$this->_update_details = false;
--- a/post-smtp/freemius/includes/entities/class-fs-plugin-plan.php
+++ b/post-smtp/freemius/includes/entities/class-fs-plugin-plan.php
@@ -13,7 +13,6 @@
/**
* Class FS_Plugin_Plan
*
- * @property FS_Pricing[] $pricing
*/
class FS_Plugin_Plan extends FS_Entity {
--- a/post-smtp/freemius/includes/entities/class-fs-site.php
+++ b/post-smtp/freemius/includes/entities/class-fs-site.php
@@ -10,16 +10,16 @@
exit;
}
- /**
- * @property int $blog_id
- */
- #[AllowDynamicProperties]
class FS_Site extends FS_Scope_Entity {
/**
* @var number
*/
public $site_id;
/**
+ * @var int
+ */
+ public $blog_id;
+ /**
* @var number
*/
public $plugin_id;
@@ -190,7 +190,7 @@
fs_ends_with( $subdomain, '.cloudwaysapps.com' ) ||
// Kinsta
(
- ( fs_starts_with( $subdomain, 'staging-' ) || fs_starts_with( $subdomain, 'env-' ) ) &&
+ ( fs_starts_with( $subdomain, 'stg-' ) || fs_starts_with( $subdomain, 'staging-' ) || fs_starts_with( $subdomain, 'env-' ) ) &&
( fs_ends_with( $subdomain, '.kinsta.com' ) || fs_ends_with( $subdomain, '.kinsta.cloud' ) )
) ||
// DesktopServer
@@ -208,6 +208,40 @@
);
}
+ /**
+ * @author Leo Fajardo (@leorw)
+ * @since 2.9.1
+ *
+ * @param string $host
+ *
+ * @return bool
+ */
+ static function is_playground_wp_environment_by_host( $host ) {
+ // Services aimed at providing a WordPress sandbox environment.
+ $sandbox_wp_environment_domains = array(
+ // InstaWP
+ 'instawp.xyz',
+
+ // TasteWP
+ 'tastewp.com',
+
+ // WordPress Playground
+ 'playground.wordpress.net',
+ );
+
+ foreach ( $sandbox_wp_environment_domains as $domain) {
+ if (
+ ( $host === $domain ) ||
+ fs_ends_with( $host, '.' . $domain ) ||
+ fs_ends_with( $host, '-' . $domain )
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
function is_localhost() {
return ( WP_FS__IS_LOCALHOST_FOR_SERVER || self::is_localhost_by_address( $this->url ) );
}
--- a/post-smtp/freemius/includes/entities/class-fs-user.php
+++ b/post-smtp/freemius/includes/entities/class-fs-user.php
@@ -48,6 +48,19 @@
parent::__construct( $user );
}
+ /**
+ * This method removes the deprecated 'is_beta' property from the serialized data.
+ * Should clean up the serialized data to avoid PHP 8.2 warning on next execution.
+ *
+ * @return void
+ */
+ function __wakeup() {
+ if ( property_exists( $this, 'is_beta' ) ) {
+ // If we enter here, and we are running PHP 8.2, we already had the warning. But we sanitize data for next execution.
+ unset( $this->is_beta );
+ }
+ }
+
function get_name() {
return trim( ucfirst( trim( is_string( $this->first ) ? $this->first : '' ) ) . ' ' . ucfirst( trim( is_string( $this->last ) ? $this->last : '' ) ) );
}
--- a/post-smtp/freemius/includes/managers/class-fs-admin-menu-manager.php
+++ b/post-smtp/freemius/includes/managers/class-fs-admin-menu-manager.php
@@ -699,16 +699,36 @@
$menu = $this->find_main_submenu();
}
+ $menu_slug = $menu['menu'][2];
$parent_slug = isset( $menu['parent_slug'] ) ?
- $menu['parent_slug'] :
- 'admin.php';
+ $menu['parent_slug'] :
+ 'admin.php';
- return admin_url(
- $parent_slug .
- ( false === strpos( $parent_slug, '?' ) ? '?' : '&' ) .
- 'page=' .
- $menu['menu'][2]
- );
+ if ( fs_apply_filter( $this->_module_unique_affix, 'enable_cpt_advanced_menu_logic', false ) ) {
+ $parent_slug = 'admin.php';
+
+ /**
+ * This line and the `if` block below it are based on the `menu_page_url()` function of WordPress.
+ *
+ * @author Leo Fajardo (@leorw)
+ * @since 2.10.2
+ */
+ global $_parent_pages;
+
+ if ( ! empty( $_parent_pages[ $menu_slug ] ) ) {
+ $_parent_slug = $_parent_pages[ $menu_slug ];
+ $parent_slug = isset( $_parent_pages[ $_parent_slug ] ) ?
+ $parent_slug :
+ $menu['parent_slug'];
+ }
+ }
+
+ return admin_url(
+ $parent_slug .
+ ( false === strpos( $parent_slug, '?' ) ? '?' : '&' ) .
+ 'page=' .
+ $menu_slug
+ );
}
/**
--- a/post-smtp/freemius/includes/managers/class-fs-admin-notice-manager.php
+++ b/post-smtp/freemius/includes/managers/class-fs-admin-notice-manager.php
@@ -194,8 +194,14 @@
* @since 1.0.7
*/
static function _add_sticky_dismiss_javascript() {
+ $sticky_admin_notice_js_template_name = 'sticky-admin-notice-js.php';
+
+ if ( ! file_exists( fs_get_template_path( $sticky_admin_notice_js_template_name ) ) ) {
+ return;
+ }
+
$params = array();
- fs_require_once_template( 'sticky-admin-notice-js.php', $params );
+ fs_require_once_template( $sticky_admin_notice_js_template_name, $params );
}
private static $_added_sticky_javascript = false;
--- a/post-smtp/freemius/start.php
+++ b/post-smtp/freemius/start.php
@@ -15,7 +15,7 @@
*
* @var string
*/
- $this_sdk_version = '2.9.0';
+ $this_sdk_version = '2.11.0';
#region SDK Selection Logic --------------------------------------------------------------------
@@ -36,7 +36,16 @@
require_once dirname( __FILE__ ) . '/includes/fs-essential-functions.php';
}
- /**
+ /**
+ * We updated the logic to support SDK loading from a subfolder of a theme as well as from a parent theme
+ * If the SDK is found in the active theme, it sets the relative path accordingly.
+ * If not, it checks the parent theme and sets the relative path if found there.
+ * This allows the SDK to be loaded from composer dependencies or from a custom `vendor/freemius` folder.
+ *
+ * @author Daniele Alessandra (@DanieleAlessandra)
+ * @since 2.9.0.5
+ *
+ *
* This complex logic fixes symlink issues (e.g. with Vargant). The logic assumes
* that if it's a file from an SDK running in a theme, the location of the SDK
* is in the main theme's folder.
@@ -83,16 +92,50 @@
*/
$themes_directory = get_theme_root( get_stylesheet() );
$themes_directory_name = basename( $themes_directory );
- $theme_candidate_basename = basename( dirname( $fs_root_path ) ) . '/' . basename( $fs_root_path );
- if ( $file_path == fs_normalize_path( realpath( trailingslashit( $themes_directory ) . $theme_candidate_basename . '/' . basename( $file_path ) ) )
- ) {
- $this_sdk_relative_path = '../' . $themes_directory_name . '/' . $theme_candidate_basename;
- $is_theme = true;
- } else {
- $this_sdk_relative_path = plugin_basename( $fs_root_path );
- $is_theme = false;
- }
+ // This change ensures that the condition works even if the SDK is located in a subdirectory (e.g., vendor)
+ $theme_candidate_sdk_basename = str_replace( $themes_directory . '/' . get_stylesheet() . '/', '', $fs_root_path );
+
+ // Check if the current file is part of the active theme.
+ $is_current_sdk_from_active_theme = $file_path == $themes_directory . '/' . get_stylesheet() . '/' . $theme_candidate_sdk_basename . '/' . basename( $file_path );
+ $is_current_sdk_from_parent_theme = false;
+
+ // Check if the current file is part of the parent theme.
+ if ( ! $is_current_sdk_from_active_theme ) {
+ $theme_candidate_sdk_basename = str_replace( $themes_directory . '/' . get_template() . '/',
+ '',
+ $fs_root_path );
+ $is_current_sdk_from_parent_theme = $file_path == $themes_directory . '/' . get_template() . '/' . $theme_candidate_sdk_basename . '/' . basename( $file_path );
+ }
+
+ $theme_name = null;
+ if ( $is_current_sdk_from_active_theme ) {
+ $theme_name = get_stylesheet();
+ $this_sdk_relative_path = '../' . $themes_directory_name . '/' . $theme_name . '/' . $theme_candidate_sdk_basename;
+ $is_theme = true;
+ } else if ( $is_current_sdk_from_parent_theme ) {
+ $theme_name = get_template();
+ $this_sdk_relative_path = '../' . $themes_directory_name . '/' . $theme_name . '/' . $theme_candidate_sdk_basename;
+ $is_theme = true;
+ } else {
+ $this_sdk_relative_path = plugin_basename( $fs_root_path );
+ $is_theme = false;
+
+ /**
+ * If this file was included from another plugin with lower SDK version, and if this plugin is symlinked, then we need to get the actual plugin path,
+ * as the value right now will be wrong, it will only remove the directory separator from the file_path.
+ *
+ * The check of `fs_find_direct_caller_plugin_file` determines that this file was indeed included by a different plugin than the main plugin.
+ */
+ if ( DIRECTORY_SEPARATOR . $this_sdk_relative_path === $fs_root_path && function_exists( 'fs_find_direct_caller_plugin_file' ) ) {
+ $original_plugin_dir_name = dirname( fs_find_direct_caller_plugin_file( $file_path ) );
+
+ // Remove everything before the original plugin directory name.
+ $this_sdk_relative_path = substr( $this_sdk_relative_path, strpos( $this_sdk_relative_path, $original_plugin_dir_name ) );
+
+ unset( $original_plugin_dir_name );
+ }
+ }
if ( ! isset( $fs_active_plugins ) ) {
// Load all Freemius powered active plugins.
@@ -176,7 +219,8 @@
$this_sdk_version != $fs_active_plugins->plugins[ $this_sdk_relative_path ]->version
) {
if ( $is_theme ) {
- $plugin_path = basename( dirname( $this_sdk_relative_path ) );
+ // Saving relative path and not only directory name as it could be a subfolder
+ $plugin_path = $theme_name;
} else {
$plugin_path = plugin_basename( fs_find_direct_caller_plugin_file( $file_path ) );
}
@@ -225,11 +269,23 @@
$is_newest_sdk_type_theme = ( isset( $fs_newest_sdk->type ) && 'theme' === $fs_newest_sdk->type );
- if ( ! $is_newest_sdk_type_theme ) {
- $is_newest_sdk_plugin_active = is_plugin_active( $fs_newest_sdk->plugin_path );
- } else {
- $current_theme = wp_get_theme();
- $is_newest_sdk_plugin_active = ( $current_theme->stylesheet === $fs_newest_sdk->plugin_path );
+ /**
+ * @var bool $is_newest_sdk_module_active
+ * True if the plugin with the newest SDK is active.
+ * True if the newest SDK is part of the current theme or current theme's parent.
+ * False otherwise.
+ */
+ if ( ! $is_newest_sdk_type_theme ) {
+ $is_newest_sdk_module_active = is_plugin_active( $fs_newest_sdk->plugin_path );
+ } else {
+ $current_theme = wp_get_theme();
+ // Detect if current theme is the one registered as newer SDK
+ $is_newest_sdk_module_active = (
+ strpos(
+ $fs_newest_sdk->plugin_path,
+ '../' . $themes_directory_name . '/' . $current_theme->get_stylesheet() . '/'
+ ) === 0
+ );
$current_theme_parent = $current_theme->parent();
@@ -237,13 +293,19 @@
* If the current theme is a child of the theme that has the newest SDK, this prevents a redirects loop
* from happening by keeping the SDK info stored in the `fs_active_plugins` option.
*/
- if ( ! $is_newest_sdk_plugin_active && $current_theme_parent instanceof WP_Theme ) {
- $is_newest_sdk_plugin_active = ( $fs_newest_sdk->plugin_path === $current_theme_parent->stylesheet );
+ if ( ! $is_newest_sdk_module_active && $current_theme_parent instanceof WP_Theme ) {
+ // Detect if current theme parent is the one registered as newer SDK
+ $is_newest_sdk_module_active = (
+ strpos(
+ $fs_newest_sdk->plugin_path,
+ '../' . $themes_directory_name . '/' . $current_theme_parent->get_stylesheet() . '/'
+ ) === 0
+ );
}
}
if ( $is_current_sdk_newest &&
- ! $is_newest_sdk_plugin_active &&
+ ! $is_newest_sdk_module_active &&
! $fs_active_plugins->newest->in_activation
) {
// If current SDK is the newest and the plugin is NOT active, it means
@@ -262,14 +324,14 @@
. '/start.php' );
}
- $is_newest_sdk_path_valid = ( $is_newest_sdk_plugin_active || $fs_active_plugins->newest->in_activation ) && file_exists( $sdk_star