diff --git a/includes/class-freemius.php b/includes/class-freemius.php index 2775cfa2d..a8265372c 100755 --- a/includes/class-freemius.php +++ b/includes/class-freemius.php @@ -2630,26 +2630,28 @@ private static function _load_required_static() { self::$_accounts = FS_Options::instance( WP_FS__ACCOUNTS_OPTION_NAME, true ); if ( is_multisite() ) { + $has_skipped_migration = ( + // 'id_slug_type_path_map' - was never stored on older versions, therefore, not exists on the site level. + null === self::$_accounts->get_option( 'id_slug_type_path_map', null, false ) && + // 'file_slug_map' stored on the site level, so it was running an SDK version before it was integrated with MS-network. + null !== self::$_accounts->get_option( 'file_slug_map', null, false ) + ); + /** - * If the id_slug_type_path_map exists on the site level but doesn't exist on the + * If the file_slug_map exists on the site level but doesn't exist on the * network level storage, it means that we need to process the storage with migration. * - * The code in this `if` scope will only be executed once and only for the first site that will execute it because once we migrate the storage data, id_slug_type_path_map will be already set in the network level storage. + * The code in this `if` scope will only be executed once and only for the first site that will execute it because once we migrate the storage data, file_slug_map will be already set in the network level storage. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ - if ( null === self::$_accounts->get_option( 'id_slug_type_path_map', null, true ) && - null !== self::$_accounts->get_option( 'id_slug_type_path_map', null, false ) + if ( + ( $has_skipped_migration && true !== self::$_accounts->get_option( 'ms_migration_complete', false, true ) ) || + ( null === self::$_accounts->get_option( 'file_slug_map', null, true ) && + null !== self::$_accounts->get_option( 'file_slug_map', null, false ) ) ) { - self::migrate_accounts_to_network(); - - // Migrate API options from site level to network level. - $api_network_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true, true ); - $api_network_options->migrate_to_network(); - - // Migrate API cache to network level storage. - FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME )->migrate_to_network(); + self::migrate_options_to_network(); } } @@ -2679,6 +2681,24 @@ private static function _load_required_static() { self::$_statics_loaded = true; } + /** + * @author Leo Fajardo (@leorw) + * + * @since 2.1.3 + */ + private static function migrate_options_to_network() { + self::migrate_accounts_to_network(); + + // Migrate API options from site level to network level. + $api_network_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true, true ); + $api_network_options->migrate_to_network(); + + // Migrate API cache to network level storage. + FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME )->migrate_to_network(); + + self::$_accounts->set_option( 'ms_migration_complete', true, true ); + } + #---------------------------------------------------------------------------------- #region Localization #---------------------------------------------------------------------------------- @@ -2905,6 +2925,10 @@ static function _debug_page_actions() { } fs_redirect( $download_url ); + } else if ( fs_request_is_action( 'migrate_options_to_network' ) ) { + check_admin_referer( 'migrate_options_to_network' ); + + self::migrate_options_to_network(); } } @@ -5987,7 +6011,7 @@ private function get_install_sync_cron_blog_id() { * @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 ) { - $this->schedule_cron( 'install_sync', 'install_sync', 'single', 0, false, $except_blog_id ); + $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id ); } /** @@ -10574,7 +10598,7 @@ function _add_premium_version_upgrade_selection() { return; } - if ( ! $this->is_premium() || $this->has_active_valid_license() ) { + if ( ! $this->is_premium() || $this->has_any_active_valid_license() ) { // This is relevant only to the free versions and premium versions without an active license. return; } @@ -16289,14 +16313,62 @@ function has_active_license() { * @since 1.2.1 */ function has_active_valid_license() { + return self::is_active_valid_license( $this->_license ); + } + + /** + * Check if a given license is active & valid (not expired). + * + * @author Vova Feldman (@svovaf) + * @since 2.1.3 + * + * @param FS_Plugin_License $license + * + * @return bool + */ + private static function is_active_valid_license( $license ) { return ( - is_object( $this->_license ) && - is_numeric( $this->_license->id ) && - $this->_license->is_active() && - $this->_license->is_valid() + is_object( $license ) && + FS_Plugin_License::is_valid_id( $license->id ) && + $license->is_active() && + $license->is_valid() ); } + /** + * Checks if there's any site that is associated with an active & valid license. + * This logic is used to determine if the admin can download the premium code base from a network level admin. + * + * @author Vova Feldman (@svovaf) + * @since 2.1.3 + * + * @return bool + */ + function has_any_active_valid_license() { + if ( ! fs_is_network_admin() ) { + return $this->has_active_valid_license(); + } + + $installs = $this->get_blog_install_map(); + $all_plugin_licenses = self::get_all_licenses( $this->_module_id ); + + foreach ( $installs as $blog_id => $install ) { + if ( ! FS_Plugin_License::is_valid_id( $install->license_id ) ) { + continue; + } + + foreach ( $all_plugin_licenses as $license ) { + if ( $license->id == $install->license_id ) { + if ( self::is_active_valid_license( $license ) ) { + return true; + } + } + } + } + + return false; + } + /** * Check if site assigned with license with enabled features. * @@ -17290,7 +17362,7 @@ private function _is_addon_id( $plugin_id ) { * @return bool */ private function _can_download_premium() { - return $this->has_active_valid_license() || + return $this->has_any_active_valid_license() || ( $this->is_trial() && ! $this->get_trial_plan()->is_free() ); } diff --git a/includes/class-fs-plugin-updater.php b/includes/class-fs-plugin-updater.php index af9eb263f..5aba1701f 100755 --- a/includes/class-fs-plugin-updater.php +++ b/includes/class-fs-plugin-updater.php @@ -82,7 +82,7 @@ private function filters() { $this->add_transient_filters(); - if ( ! $this->_fs->has_active_valid_license() ) { + if ( ! $this->_fs->has_any_active_valid_license() ) { /** * If user has the premium plugin's code but do NOT have an active license, * encourage him to upgrade by showing that there's a new release, but instead @@ -114,7 +114,7 @@ private function filters() { add_filter( 'upgrader_post_install', array( &$this, '_maybe_update_folder_name' ), 10, 3 ); } - if ( ! $this->_fs->has_active_valid_license() ) { + if ( ! $this->_fs->has_any_active_valid_license() ) { add_filter( 'wp_prepare_themes_for_js', array( &$this, 'change_theme_update_info_html' ), 10, 1 ); } } diff --git a/includes/entities/class-fs-site.php b/includes/entities/class-fs-site.php index c8954923b..c74bb55ba 100755 --- a/includes/entities/class-fs-site.php +++ b/includes/entities/class-fs-site.php @@ -150,6 +150,7 @@ static function is_localhost_by_address( $url ) { fs_starts_with( $subdomain, 'local.' ) || fs_starts_with( $subdomain, 'dev.' ) || fs_starts_with( $subdomain, 'test.' ) || + fs_starts_with( $subdomain, 'stage.' ) || fs_starts_with( $subdomain, 'staging.' ) || // Ends with. @@ -171,7 +172,9 @@ static function is_localhost_by_address( $url ) { ( fs_ends_with($subdomain, 'pantheonsite.io') && (fs_starts_with($subdomain, 'test-') || fs_starts_with($subdomain, 'dev-'))) || // Cloudways - fs_ends_with( $subdomain, '.cloudwaysapps.com' ) + fs_ends_with( $subdomain, '.cloudwaysapps.com' ) || + // Kinsta + (fs_ends_with($subdomain, '.kinsta.com') && fs_starts_with($subdomain, 'staging-')) ); } diff --git a/includes/fs-plugin-info-dialog.php b/includes/fs-plugin-info-dialog.php index 366ffa60e..489bb4c3e 100755 --- a/includes/fs-plugin-info-dialog.php +++ b/includes/fs-plugin-info-dialog.php @@ -203,7 +203,7 @@ function _get_addon_info_filter( $data, $action = '', $args = null ) { if ( is_object( $latest ) ) { $data->version = $latest->version; - $data->last_updated = ! is_null( $latest->updated ) ? $latest->updated : $latest->created; + $data->last_updated = $latest->created; $data->requires = $latest->requires_platform_version; $data->tested = $latest->tested_up_to_version; } else { diff --git a/start.php b/start.php index 68a7e2d82..dc7a50266 100644 --- a/start.php +++ b/start.php @@ -15,7 +15,7 @@ * * @var string */ - $this_sdk_version = '2.1.2'; + $this_sdk_version = '2.1.3'; #region SDK Selection Logic -------------------------------------------------------------------- diff --git a/templates/debug.php b/templates/debug.php index 89ae7eb3b..0ee3ebf9c 100644 --- a/templates/debug.php +++ b/templates/debug.php @@ -86,6 +86,16 @@ + get_option( 'ms_migration_complete', false, true ) ) : ?> + + +
+ + + +
+ + diff --git a/templates/forms/deactivation/form.php b/templates/forms/deactivation/form.php index 0e74617e9..93f08b32b 100755 --- a/templates/forms/deactivation/form.php +++ b/templates/forms/deactivation/form.php @@ -94,7 +94,7 @@ isAnonymous = , otherReasonID = , dontShareDataReasonID = , - deleteThemeUpdateData = is_theme() && $fs->is_premium() && ! $fs->has_active_valid_license() ? 'true' : 'false' ?>; + deleteThemeUpdateData = is_theme() && $fs->is_premium() && ! $fs->has_any_active_valid_license() ? 'true' : 'false' ?>; $modal.appendTo($('body'));