This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,477 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 1.0.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class FS_Admin_Notice_Manager {
/**
* @since 1.2.2
*
* @var string
*/
protected $_module_unique_affix;
/**
* @var string
*/
protected $_id;
/**
* @var string
*/
protected $_title;
/**
* @var array[string]array
*/
private $_notices = array();
/**
* @var FS_Key_Value_Storage
*/
private $_sticky_storage;
/**
* @var FS_Logger
*/
protected $_logger;
/**
* @since 2.0.0
* @var int The ID of the blog that is associated with the current site level admin notices.
*/
private $_blog_id = 0;
/**
* @since 2.0.0
* @var bool
*/
private $_is_network_notices;
/**
* @var FS_Admin_Notice_Manager[]
*/
private static $_instances = array();
/**
* @param string $id
* @param string $title
* @param string $module_unique_affix
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on
* network and blog admin pages.
* @param bool $network_level_or_blog_id Since 2.0.0
*
* @return \FS_Admin_Notice_Manager
*/
static function instance(
$id,
$title = '',
$module_unique_affix = '',
$is_network_and_blog_admins = false,
$network_level_or_blog_id = false
) {
if ( $is_network_and_blog_admins ) {
$network_level_or_blog_id = true;
}
$key = strtolower( $id );
if ( is_multisite() ) {
if ( true === $network_level_or_blog_id ) {
$key .= ':ms';
} else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) {
$key .= ":{$network_level_or_blog_id}";
} else {
$network_level_or_blog_id = get_current_blog_id();
$key .= ":{$network_level_or_blog_id}";
}
}
if ( ! isset( self::$_instances[ $key ] ) ) {
self::$_instances[ $key ] = new FS_Admin_Notice_Manager(
$id,
$title,
$module_unique_affix,
$is_network_and_blog_admins,
$network_level_or_blog_id
);
}
return self::$_instances[ $key ];
}
/**
* @param string $id
* @param string $title
* @param string $module_unique_affix
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and
* blog admin pages.
* @param bool|int $network_level_or_blog_id
*/
protected function __construct(
$id,
$title = '',
$module_unique_affix = '',
$is_network_and_blog_admins = false,
$network_level_or_blog_id = false
) {
$this->_id = $id;
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $this->_id . '_data', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
$this->_title = ! empty( $title ) ? $title : '';
$this->_module_unique_affix = $module_unique_affix;
$this->_sticky_storage = FS_Key_Value_Storage::instance( 'admin_notices', $this->_id, $network_level_or_blog_id );
if ( is_multisite() ) {
$this->_is_network_notices = ( true === $network_level_or_blog_id );
if ( is_numeric( $network_level_or_blog_id ) ) {
$this->_blog_id = $network_level_or_blog_id;
}
} else {
$this->_is_network_notices = false;
}
$is_network_admin = fs_is_network_admin();
$is_blog_admin = fs_is_blog_admin();
if ( ( $this->_is_network_notices && $is_network_admin ) ||
( ! $this->_is_network_notices && $is_blog_admin ) ||
( $is_network_and_blog_admins && ( $is_network_admin || $is_blog_admin ) )
) {
if ( 0 < count( $this->_sticky_storage ) ) {
$ajax_action_suffix = str_replace( ':', '-', $this->_id );
// If there are sticky notices for the current slug, add a callback
// to the AJAX action that handles message dismiss.
add_action( "wp_ajax_fs_dismiss_notice_action_{$ajax_action_suffix}", array(
&$this,
'dismiss_notice_ajax_callback'
) );
foreach ( $this->_sticky_storage as $msg ) {
// Add admin notice.
$this->add(
$msg['message'],
$msg['title'],
$msg['type'],
true,
$msg['id'],
false,
isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null,
! empty( $msg['plugin'] ) ? $msg['plugin'] : null,
$is_network_and_blog_admins
);
}
}
}
}
/**
* Remove sticky message by ID.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
*/
function dismiss_notice_ajax_callback() {
check_admin_referer( 'fs_dismiss_notice_action' );
if ( ! is_numeric( $_POST['message_id'] ) ) {
$this->_sticky_storage->remove( $_POST['message_id'] );
}
wp_die();
}
/**
* Rendered sticky message dismiss JavaScript.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*/
static function _add_sticky_dismiss_javascript() {
$params = array();
fs_require_once_template( 'sticky-admin-notice-js.php', $params );
}
private static $_added_sticky_javascript = false;
/**
* Hook to the admin_footer to add sticky message dismiss JavaScript handler.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*/
private static function has_sticky_messages() {
if ( ! self::$_added_sticky_javascript ) {
add_action( 'admin_footer', array( 'FS_Admin_Notice_Manager', '_add_sticky_dismiss_javascript' ) );
}
}
/**
* Handle admin_notices by printing the admin messages stacked in the queue.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.4
*
*/
function _admin_notices_hook() {
if ( function_exists( 'current_user_can' ) &&
! current_user_can( 'manage_options' )
) {
// Only show messages to admins.
return;
}
$show_admin_notices = ( ! $this->is_gutenberg_page() );
foreach ( $this->_notices as $id => $msg ) {
if ( isset( $msg['wp_user_id'] ) && is_numeric( $msg['wp_user_id'] ) ) {
if ( get_current_user_id() != $msg['wp_user_id'] ) {
continue;
}
}
/**
* Added a filter to control the visibility of admin notices.
*
* Usage example:
*
* /**
* * @param bool $show
* * @param array $msg {
* * @var string $message The actual message.
* * @var string $title An optional message title.
* * @var string $type The type of the message ('success', 'update', 'warning', 'promotion').
* * @var string $id The unique identifier of the message.
* * @var string $manager_id The unique identifier of the notices manager. For plugins it would be the plugin's slug, for themes - `<slug>-theme`.
* * @var string $plugin The product's title.
* * @var string $wp_user_id An optional WP user ID that this admin notice is for.
* * }
* *
* * @return bool
* *\/
* function my_custom_show_admin_notice( $show, $msg ) {
* if ('trial_promotion' != $msg['id']) {
* return false;
* }
*
* return $show;
* }
*
* my_fs()->add_filter( 'show_admin_notice', 'my_custom_show_admin_notice', 10, 2 );
*
* @author Vova Feldman
* @since 2.2.0
*/
$show_notice = call_user_func_array( 'fs_apply_filter', array(
$this->_module_unique_affix,
'show_admin_notice',
$show_admin_notices,
$msg
) );
if ( true !== $show_notice ) {
continue;
}
fs_require_template( 'admin-notice.php', $msg );
if ( $msg['sticky'] ) {
self::has_sticky_messages();
}
}
}
/**
* Enqueue common stylesheet to style admin notice.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*/
function _enqueue_styles() {
fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
}
/**
* Check if the current page is the Gutenberg block editor.
*
* @author Vova Feldman (@svovaf)
* @since 2.2.3
*
* @return bool
*/
function is_gutenberg_page() {
if ( function_exists( 'is_gutenberg_page' ) &&
is_gutenberg_page()
) {
// The Gutenberg plugin is on.
return true;
}
$current_screen = get_current_screen();
if ( method_exists( $current_screen, 'is_block_editor' ) &&
$current_screen->is_block_editor()
) {
// Gutenberg page on 5+.
return true;
}
return false;
}
/**
* Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.4
*
* @param string $message
* @param string $title
* @param string $type
* @param bool $is_sticky
* @param string $id Message ID
* @param bool $store_if_sticky
* @param number|null $wp_user_id
* @param string|null $plugin_title
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
* and blog admin pages.
*
* @uses add_action()
*/
function add(
$message,
$title = '',
$type = 'success',
$is_sticky = false,
$id = '',
$store_if_sticky = true,
$wp_user_id = null,
$plugin_title = null,
$is_network_and_blog_admins = false
) {
$notices_type = $this->get_notices_type();
if ( empty( $this->_notices ) ) {
if ( ! $is_network_and_blog_admins ) {
add_action( $notices_type, array( &$this, "_admin_notices_hook" ) );
} else {
add_action( 'network_admin_notices', array( &$this, "_admin_notices_hook" ) );
add_action( 'admin_notices', array( &$this, "_admin_notices_hook" ) );
}
add_action( 'admin_enqueue_scripts', array( &$this, '_enqueue_styles' ) );
}
if ( '' === $id ) {
$id = md5( $title . ' ' . $message . ' ' . $type );
}
$message_object = array(
'message' => $message,
'title' => $title,
'type' => $type,
'sticky' => $is_sticky,
'id' => $id,
'manager_id' => $this->_id,
'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ),
'wp_user_id' => $wp_user_id,
);
if ( $is_sticky && $store_if_sticky ) {
$this->_sticky_storage->{$id} = $message_object;
}
$this->_notices[ $id ] = $message_object;
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param string|string[] $ids
*/
function remove_sticky( $ids ) {
if ( ! is_array( $ids ) ) {
$ids = array( $ids );
}
foreach ( $ids as $id ) {
// Remove from sticky storage.
$this->_sticky_storage->remove( $id );
if ( isset( $this->_notices[ $id ] ) ) {
unset( $this->_notices[ $id ] );
}
}
}
/**
* Check if sticky message exists by id.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.9
*
* @param $id
*
* @return bool
*/
function has_sticky( $id ) {
return isset( $this->_sticky_storage[ $id ] );
}
/**
* Adds sticky admin notification.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param string $message
* @param string $id Message ID
* @param string $title
* @param string $type
* @param number|null $wp_user_id
* @param string|null $plugin_title
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
* and blog admin pages.
*/
function add_sticky( $message, $id, $title = '', $type = 'success', $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false ) {
if ( ! empty( $this->_module_unique_affix ) ) {
$message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message );
$title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title );
}
$this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins );
}
/**
* Clear all sticky messages.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.8
*/
function clear_all_sticky() {
$this->_sticky_storage->clear_all();
}
#--------------------------------------------------------------------------------
#region Helper Method
#--------------------------------------------------------------------------------
/**
* @author Vova Feldman (@svovaf)
* @since 2.0.0
*
* @return string
*/
private function get_notices_type() {
return $this->_is_network_notices ?
'network_admin_notices' :
'admin_notices';
}
#endregion
}

View File

@ -0,0 +1,326 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 1.1.6
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class FS_Cache_Manager {
/**
* @var FS_Option_Manager
*/
private $_options;
/**
* @var FS_Logger
*/
private $_logger;
/**
* @var FS_Cache_Manager[]
*/
private static $_MANAGERS = array();
/**
* @author Vova Feldman (@svovaf)
* @since 1.1.3
*
* @param string $id
*/
private function __construct( $id ) {
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_cach_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
$this->_logger->entrance();
$this->_logger->log( 'id = ' . $id );
$this->_options = FS_Option_Manager::get_manager( $id, true, true, false );
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @param $id
*
* @return FS_Cache_Manager
*/
static function get_manager( $id ) {
$id = strtolower( $id );
if ( ! isset( self::$_MANAGERS[ $id ] ) ) {
self::$_MANAGERS[ $id ] = new FS_Cache_Manager( $id );
}
return self::$_MANAGERS[ $id ];
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @return bool
*/
function is_empty() {
$this->_logger->entrance();
return $this->_options->is_empty();
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*/
function clear() {
$this->_logger->entrance();
$this->_options->clear( true );
}
/**
* Delete cache manager from DB.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.9
*/
function delete() {
$this->_options->delete();
}
/**
* Check if there's a cached item.
*
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @param string $key
*
* @return bool
*/
function has( $key ) {
$cache_entry = $this->_options->get_option( $key, false );
return ( is_object( $cache_entry ) &&
isset( $cache_entry->timestamp ) &&
is_numeric( $cache_entry->timestamp )
);
}
/**
* Check if there's a valid cached item.
*
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @param string $key
* @param null|int $expiration Since 1.2.2.7
*
* @return bool
*/
function has_valid( $key, $expiration = null ) {
$cache_entry = $this->_options->get_option( $key, false );
$is_valid = ( is_object( $cache_entry ) &&
isset( $cache_entry->timestamp ) &&
is_numeric( $cache_entry->timestamp ) &&
$cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
);
if ( $is_valid &&
is_numeric( $expiration ) &&
isset( $cache_entry->created ) &&
is_numeric( $cache_entry->created ) &&
$cache_entry->created + $expiration < WP_FS__SCRIPT_START_TIME
) {
/**
* Even if the cache is still valid, since we are checking for validity
* with an explicit expiration period, if the period has past, return
* `false` as if the cache is invalid.
*
* @since 1.2.2.7
*/
$is_valid = false;
}
return $is_valid;
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
function get( $key, $default = null ) {
$this->_logger->entrance( 'key = ' . $key );
$cache_entry = $this->_options->get_option( $key, false );
if ( is_object( $cache_entry ) &&
isset( $cache_entry->timestamp ) &&
is_numeric( $cache_entry->timestamp )
) {
return $cache_entry->result;
}
return is_object( $default ) ? clone $default : $default;
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
function get_valid( $key, $default = null ) {
$this->_logger->entrance( 'key = ' . $key );
$cache_entry = $this->_options->get_option( $key, false );
if ( is_object( $cache_entry ) &&
isset( $cache_entry->timestamp ) &&
is_numeric( $cache_entry->timestamp ) &&
$cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
) {
return $cache_entry->result;
}
return is_object( $default ) ? clone $default : $default;
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @param string $key
* @param mixed $value
* @param int $expiration
* @param int $created Since 2.0.0 Cache creation date.
*/
function set( $key, $value, $expiration = WP_FS__TIME_24_HOURS_IN_SEC, $created = WP_FS__SCRIPT_START_TIME ) {
$this->_logger->entrance( 'key = ' . $key );
$cache_entry = new stdClass();
$cache_entry->result = $value;
$cache_entry->created = $created;
$cache_entry->timestamp = $created + $expiration;
$this->_options->set_option( $key, $cache_entry, true );
}
/**
* Get cached record expiration, or false if not cached or expired.
*
* @author Vova Feldman (@svovaf)
* @since 1.1.7.3
*
* @param string $key
*
* @return bool|int
*/
function get_record_expiration( $key ) {
$this->_logger->entrance( 'key = ' . $key );
$cache_entry = $this->_options->get_option( $key, false );
if ( is_object( $cache_entry ) &&
isset( $cache_entry->timestamp ) &&
is_numeric( $cache_entry->timestamp ) &&
$cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
) {
return $cache_entry->timestamp;
}
return false;
}
/**
* Purge cached item.
*
* @author Vova Feldman (@svovaf)
* @since 1.1.6
*
* @param string $key
*/
function purge( $key ) {
$this->_logger->entrance( 'key = ' . $key );
$this->_options->unset_option( $key, true );
}
/**
* Extend cached item caching period.
*
* @author Vova Feldman (@svovaf)
* @since 2.0.0
*
* @param string $key
* @param int $expiration
*
* @return bool
*/
function update_expiration( $key, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) {
$this->_logger->entrance( 'key = ' . $key );
$cache_entry = $this->_options->get_option( $key, false );
if ( ! is_object( $cache_entry ) ||
! isset( $cache_entry->timestamp ) ||
! is_numeric( $cache_entry->timestamp )
) {
return false;
}
$this->set( $key, $cache_entry->result, $expiration, $cache_entry->created );
return true;
}
/**
* Set cached item as expired.
*
* @author Vova Feldman (@svovaf)
* @since 1.2.2.7
*
* @param string $key
*/
function expire( $key ) {
$this->_logger->entrance( 'key = ' . $key );
$cache_entry = $this->_options->get_option( $key, false );
if ( is_object( $cache_entry ) &&
isset( $cache_entry->timestamp ) &&
is_numeric( $cache_entry->timestamp )
) {
// Set to expired.
$cache_entry->timestamp = WP_FS__SCRIPT_START_TIME;
$this->_options->set_option( $key, $cache_entry, true );
}
}
#--------------------------------------------------------------------------------
#region Migration
#--------------------------------------------------------------------------------
/**
* Migrate options from site level.
*
* @author Vova Feldman (@svovaf)
* @since 2.0.0
*/
function migrate_to_network() {
$this->_options->migrate_to_network();
}
#endregion
}

View File

@ -0,0 +1,202 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 2.1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class FS_GDPR_Manager {
/**
* @var FS_Option_Manager
*/
private $_storage;
/**
* @var array {
* @type bool $required Are GDPR rules apply on the current context admin.
* @type bool $show_opt_in_notice Should the marketing and offers opt-in message be shown to the admin or not. If not set, defaults to `true`.
* @type int $notice_shown_at Last time the special GDPR opt-in message was shown to the current admin.
* }
*/
private $_data;
/**
* @var int
*/
private $_wp_user_id;
/**
* @var string
*/
private $_option_name;
/**
* @var FS_Admin_Notices
*/
private $_notices;
#--------------------------------------------------------------------------------
#region Singleton
#--------------------------------------------------------------------------------
/**
* @var FS_GDPR_Manager
*/
private static $_instance;
/**
* @return FS_GDPR_Manager
*/
public static function instance() {
if ( ! isset( self::$_instance ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
#endregion
private function __construct() {
$this->_storage = FS_Option_Manager::get_manager( WP_FS__GDPR_OPTION_NAME, true, true );
$this->_wp_user_id = Freemius::get_current_wp_user_id();
$this->_option_name = "u{$this->_wp_user_id}";
$this->_data = $this->_storage->get_option( $this->_option_name, array() );
$this->_notices = FS_Admin_Notices::instance( 'all_admins', '', '', true );
if ( ! is_array( $this->_data ) ) {
$this->_data = array();
}
}
/**
* Update a GDPR option for the current admin and store it.
*
* @author Vova Feldman (@svovaf)
* @since 2.1.0
*
* @param string $name
* @param mixed $value
*/
private function update_option( $name, $value ) {
$this->_data[ $name ] = $value;
$this->_storage->set_option( $this->_option_name, $this->_data, true );
}
/**
* @author Leo Fajardo (@leorw)
* @since 2.1.0
*
* @return bool|null
*/
public function is_required() {
return isset( $this->_data['required'] ) ?
$this->_data['required'] :
null;
}
/**
* @author Leo Fajardo (@leorw)
* @since 2.1.0
*
* @param bool $is_required
*/
public function store_is_required( $is_required ) {
$this->update_option( 'required', $is_required );
}
/**
* Checks if the GDPR opt-in sticky notice is currently shown.
*
* @author Vova Feldman (@svovaf)
* @since 2.1.0
*
* @return bool
*/
public function is_opt_in_notice_shown() {
return $this->_notices->has_sticky( "gdpr_optin_actions_{$this->_wp_user_id}", true );
}
/**
* Remove the GDPR opt-in sticky notice.
*
* @author Vova Feldman (@svovaf)
* @since 2.1.0
*/
public function remove_opt_in_notice() {
$this->_notices->remove_sticky( "gdpr_optin_actions_{$this->_wp_user_id}", true );
$this->disable_opt_in_notice();
}
/**
* Prevents the opt-in message from being added/shown.
*
* @author Leo Fajardo (@leorw)
* @since 2.1.0
*/
public function disable_opt_in_notice() {
$this->update_option( 'show_opt_in_notice', false );
}
/**
* Checks if a GDPR opt-in message needs to be shown to the current admin.
*
* @author Vova Feldman (@svovaf)
* @since 2.1.0
*
* @return bool
*/
public function should_show_opt_in_notice() {
return (
! isset( $this->_data['show_opt_in_notice'] ) ||
true === $this->_data['show_opt_in_notice']
);
}
/**
* Get the last time the GDPR opt-in notice was shown.
*
* @author Vova Feldman (@svovaf)
* @since 2.1.0
*
* @return false|int
*/
public function last_time_notice_was_shown() {
return isset( $this->_data['notice_shown_at'] ) ?
$this->_data['notice_shown_at'] :
false;
}
/**
* Update the timestamp of the last time the GDPR opt-in message was shown to now.
*
* @author Vova Feldman (@svovaf)
* @since 2.1.0
*/
public function notice_was_just_shown() {
$this->update_option( 'notice_shown_at', WP_FS__SCRIPT_START_TIME );
}
/**
* @param string $message
* @param string|null $plugin_title
*
* @author Vova Feldman (@svovaf)
* @since 2.1.0
*/
public function add_opt_in_sticky_notice( $message, $plugin_title = null ) {
$this->_notices->add_sticky(
$message,
"gdpr_optin_actions_{$this->_wp_user_id}",
'',
'promotion',
true,
$this->_wp_user_id,
$plugin_title,
true
);
}
}

View File

@ -0,0 +1,392 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 1.0.7
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class FS_Key_Value_Storage
*
* @property int $install_timestamp
* @property int $activation_timestamp
* @property int $sync_timestamp
* @property object $sync_cron
* @property int $install_sync_timestamp
* @property array $connectivity_test
* @property array $is_on
* @property object $trial_plan
* @property bool $has_trial_plan
* @property bool $trial_promotion_shown
* @property string $sdk_version
* @property string $sdk_last_version
* @property bool $sdk_upgrade_mode
* @property bool $sdk_downgrade_mode
* @property bool $plugin_upgrade_mode
* @property bool $plugin_downgrade_mode
* @property string $plugin_version
* @property string $plugin_last_version
* @property bool $is_plugin_new_install
* @property bool $was_plugin_loaded
* @property object $plugin_main_file
* @property bool $prev_is_premium
* @property array $is_anonymous
* @property bool $is_pending_activation
* @property bool $sticky_optin_added
* @property object $uninstall_reason
* @property object $subscription
*/
class FS_Key_Value_Storage implements ArrayAccess, Iterator, Countable {
/**
* @var string
*/
protected $_id;
/**
* @since 1.2.2
*
* @var string
*/
protected $_secondary_id;
/**
* @since 2.0.0
* @var int The ID of the blog that is associated with the current site level options.
*/
private $_blog_id = 0;
/**
* @since 2.0.0
* @var bool
*/
private $_is_multisite_storage;
/**
* @var array
*/
protected $_data;
/**
* @var FS_Key_Value_Storage[]
*/
private static $_instances = array();
/**
* @var FS_Logger
*/
protected $_logger;
/**
* @param string $id
* @param string $secondary_id
* @param bool $network_level_or_blog_id
*
* @return FS_Key_Value_Storage
*/
static function instance( $id, $secondary_id, $network_level_or_blog_id = false ) {
$key = $id . ':' . $secondary_id;
if ( is_multisite() ) {
if ( true === $network_level_or_blog_id ) {
$key .= ':ms';
} else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) {
$key .= ":{$network_level_or_blog_id}";
} else {
$network_level_or_blog_id = get_current_blog_id();
$key .= ":{$network_level_or_blog_id}";
}
}
if ( ! isset( self::$_instances[ $key ] ) ) {
self::$_instances[ $key ] = new FS_Key_Value_Storage( $id, $secondary_id, $network_level_or_blog_id );
}
return self::$_instances[ $key ];
}
protected function __construct( $id, $secondary_id, $network_level_or_blog_id = false ) {
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $secondary_id . '_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
$this->_id = $id;
$this->_secondary_id = $secondary_id;
if ( is_multisite() ) {
$this->_is_multisite_storage = ( true === $network_level_or_blog_id );
if ( is_numeric( $network_level_or_blog_id ) ) {
$this->_blog_id = $network_level_or_blog_id;
}
} else {
$this->_is_multisite_storage = false;
}
$this->load();
}
protected function get_option_manager() {
return FS_Option_Manager::get_manager(
WP_FS__ACCOUNTS_OPTION_NAME,
true,
$this->_is_multisite_storage ?
true :
( $this->_blog_id > 0 ? $this->_blog_id : false )
);
}
protected function get_all_data() {
return $this->get_option_manager()->get_option( $this->_id, array() );
}
/**
* Load plugin data from local DB.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*/
function load() {
$all_plugins_data = $this->get_all_data();
$this->_data = isset( $all_plugins_data[ $this->_secondary_id ] ) ?
$all_plugins_data[ $this->_secondary_id ] :
array();
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param string $key
* @param mixed $value
* @param bool $flush
*/
function store( $key, $value, $flush = true ) {
if ( $this->_logger->is_on() ) {
$this->_logger->entrance( $key . ' = ' . var_export( $value, true ) );
}
if ( array_key_exists( $key, $this->_data ) && $value === $this->_data[ $key ] ) {
// No need to store data if the value wasn't changed.
return;
}
$all_data = $this->get_all_data();
$this->_data[ $key ] = $value;
$all_data[ $this->_secondary_id ] = $this->_data;
$options_manager = $this->get_option_manager();
$options_manager->set_option( $this->_id, $all_data, $flush );
}
/**
* @author Vova Feldman (@svovaf)
* @since 2.0.0
*/
function save() {
$this->get_option_manager()->store();
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param bool $store
* @param string[] $exceptions Set of keys to keep and not clear.
*/
function clear_all( $store = true, $exceptions = array() ) {
$new_data = array();
foreach ( $exceptions as $key ) {
if ( isset( $this->_data[ $key ] ) ) {
$new_data[ $key ] = $this->_data[ $key ];
}
}
$this->_data = $new_data;
if ( $store ) {
$all_data = $this->get_all_data();
$all_data[ $this->_secondary_id ] = $this->_data;
$options_manager = $this->get_option_manager();
$options_manager->set_option( $this->_id, $all_data, true );
}
}
/**
* Delete key-value storage.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.9
*/
function delete() {
$this->_data = array();
$all_data = $this->get_all_data();
unset( $all_data[ $this->_secondary_id ] );
$options_manager = $this->get_option_manager();
$options_manager->set_option( $this->_id, $all_data, true );
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param string $key
* @param bool $store
*/
function remove( $key, $store = true ) {
if ( ! array_key_exists( $key, $this->_data ) ) {
return;
}
unset( $this->_data[ $key ] );
if ( $store ) {
$all_data = $this->get_all_data();
$all_data[ $this->_secondary_id ] = $this->_data;
$options_manager = $this->get_option_manager();
$options_manager->set_option( $this->_id, $all_data, true );
}
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param string $key
* @param mixed $default
*
* @return bool|\FS_Plugin
*/
function get( $key, $default = false ) {
return array_key_exists( $key, $this->_data ) ?
$this->_data[ $key ] :
$default;
}
/**
* @author Vova Feldman (@svovaf)
* @since 2.0.0
*
* @return string
*/
function get_secondary_id() {
return $this->_secondary_id;
}
/* ArrayAccess + Magic Access (better for refactoring)
-----------------------------------------------------------------------------------*/
function __set( $k, $v ) {
$this->store( $k, $v );
}
function __isset( $k ) {
return array_key_exists( $k, $this->_data );
}
function __unset( $k ) {
$this->remove( $k );
}
function __get( $k ) {
return $this->get( $k, null );
}
function offsetSet( $k, $v ) {
if ( is_null( $k ) ) {
throw new Exception( 'Can\'t append value to request params.' );
} else {
$this->{$k} = $v;
}
}
function offsetExists( $k ) {
return array_key_exists( $k, $this->_data );
}
function offsetUnset( $k ) {
unset( $this->$k );
}
function offsetGet( $k ) {
return $this->get( $k, null );
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Return the current element
*
* @link http://php.net/manual/en/iterator.current.php
* @return mixed Can return any type.
*/
public function current() {
return current( $this->_data );
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Move forward to next element
*
* @link http://php.net/manual/en/iterator.next.php
* @return void Any returned value is ignored.
*/
public function next() {
next( $this->_data );
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Return the key of the current element
*
* @link http://php.net/manual/en/iterator.key.php
* @return mixed scalar on success, or null on failure.
*/
public function key() {
return key( $this->_data );
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Checks if current position is valid
*
* @link http://php.net/manual/en/iterator.valid.php
* @return boolean The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid() {
$key = key( $this->_data );
return ( $key !== null && $key !== false );
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Rewind the Iterator to the first element
*
* @link http://php.net/manual/en/iterator.rewind.php
* @return void Any returned value is ignored.
*/
public function rewind() {
reset( $this->_data );
}
/**
* (PHP 5 &gt;= 5.1.0)<br/>
* Count elements of an object
*
* @link http://php.net/manual/en/countable.count.php
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
*/
public function count() {
return count( $this->_data );
}
}

View File

@ -0,0 +1,104 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 1.0.6
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class FS_License_Manager /*extends FS_Abstract_Manager*/
{
//
//
// /**
// * @var FS_License_Manager[]
// */
// private static $_instances = array();
//
// static function instance( Freemius $fs ) {
// $slug = strtolower( $fs->get_slug() );
//
// if ( ! isset( self::$_instances[ $slug ] ) ) {
// self::$_instances[ $slug ] = new FS_License_Manager( $slug, $fs );
// }
//
// return self::$_instances[ $slug ];
// }
//
//// private function __construct($slug) {
//// parent::__construct($slug);
//// }
//
// function entry_id() {
// return 'licenses';
// }
//
// function sync( $id ) {
//
// }
//
// /**
// * @author Vova Feldman (@svovaf)
// * @since 1.0.5
// * @uses FS_Api
// *
// * @param number|bool $plugin_id
// *
// * @return FS_Plugin_License[]|stdClass Licenses or API error.
// */
// function api_get_user_plugin_licenses( $plugin_id = false ) {
// $api = $this->_fs->get_api_user_scope();
//
// if ( ! is_numeric( $plugin_id ) ) {
// $plugin_id = $this->_fs->get_id();
// }
//
// $result = $api->call( "/plugins/{$plugin_id}/licenses.json" );
//
// if ( ! isset( $result->error ) ) {
// for ( $i = 0, $len = count( $result->licenses ); $i < $len; $i ++ ) {
// $result->licenses[ $i ] = new FS_Plugin_License( $result->licenses[ $i ] );
// }
//
// $result = $result->licenses;
// }
//
// return $result;
// }
//
// function api_get_many() {
//
// }
//
// function api_activate( $id ) {
//
// }
//
// function api_deactivate( $id ) {
//
// }
/**
* @param FS_Plugin_License[] $licenses
*
* @return bool
*/
static function has_premium_license( $licenses ) {
if ( is_array( $licenses ) ) {
foreach ( $licenses as $license ) {
/**
* @var FS_Plugin_License $license
*/
if ( ! $license->is_utilized() && $license->is_features_enabled() ) {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,521 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 1.0.3
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* 3-layer lazy options manager.
* layer 3: Memory
* layer 2: Cache (if there's any caching plugin and if WP_FS__DEBUG_SDK is FALSE)
* layer 1: Database (options table). All options stored as one option record in the DB to reduce number of DB
* queries.
*
* If load() is not explicitly called, starts as empty manager. Same thing about saving the data - you have to
* explicitly call store().
*
* Class Freemius_Option_Manager
*/
class FS_Option_Manager {
/**
* @var string
*/
private $_id;
/**
* @var array|object
*/
private $_options;
/**
* @var FS_Logger
*/
private $_logger;
/**
* @since 2.0.0
* @var int The ID of the blog that is associated with the current site level options.
*/
private $_blog_id = 0;
/**
* @since 2.0.0
* @var bool
*/
private $_is_network_storage;
/**
* @var bool|null
*/
private $_autoload;
/**
* @var array[string]FS_Option_Manager {
* @key string
* @value FS_Option_Manager
* }
*/
private static $_MANAGERS = array();
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @param string $id
* @param bool $load
* @param bool|int $network_level_or_blog_id Since 2.0.0
* @param bool|null $autoload
*/
private function __construct(
$id,
$load = false,
$network_level_or_blog_id = false,
$autoload = null
) {
$id = strtolower( $id );
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_opt_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
$this->_logger->entrance();
$this->_logger->log( 'id = ' . $id );
$this->_id = $id;
$this->_autoload = $autoload;
if ( is_multisite() ) {
$this->_is_network_storage = ( true === $network_level_or_blog_id );
if ( is_numeric( $network_level_or_blog_id ) ) {
$this->_blog_id = $network_level_or_blog_id;
}
} else {
$this->_is_network_storage = false;
}
if ( $load ) {
$this->load();
}
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @param string $id
* @param bool $load
* @param bool|int $network_level_or_blog_id Since 2.0.0
* @param bool|null $autoload
*
* @return \FS_Option_Manager
*/
static function get_manager(
$id,
$load = false,
$network_level_or_blog_id = false,
$autoload = null
) {
$key = strtolower( $id );
if ( is_multisite() ) {
if ( true === $network_level_or_blog_id ) {
$key .= ':ms';
} else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) {
$key .= ":{$network_level_or_blog_id}";
} else {
$network_level_or_blog_id = get_current_blog_id();
$key .= ":{$network_level_or_blog_id}";
}
}
if ( ! isset( self::$_MANAGERS[ $key ] ) ) {
self::$_MANAGERS[ $key ] = new FS_Option_Manager(
$id,
$load,
$network_level_or_blog_id,
$autoload
);
} // If load required but not yet loaded, load.
else if ( $load && ! self::$_MANAGERS[ $key ]->is_loaded() ) {
self::$_MANAGERS[ $key ]->load();
}
return self::$_MANAGERS[ $key ];
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @param bool $flush
*/
function load( $flush = false ) {
$this->_logger->entrance();
$option_name = $this->get_option_manager_name();
if ( $flush || ! isset( $this->_options ) ) {
if ( isset( $this->_options ) ) {
// Clear prev options.
$this->clear();
}
$cache_group = $this->get_cache_group();
if ( WP_FS__DEBUG_SDK ) {
// Don't use cache layer in DEBUG mode.
$load_options = empty( $this->_options );
} else {
$this->_options = wp_cache_get(
$option_name,
$cache_group
);
$load_options = ( false === $this->_options );
}
$cached = true;
if ( $load_options ) {
if ( $this->_is_network_storage ) {
$this->_options = get_site_option( $option_name );
} else if ( $this->_blog_id > 0 ) {
$this->_options = get_blog_option( $this->_blog_id, $option_name );
} else {
$this->_options = get_option( $option_name );
}
if ( is_string( $this->_options ) ) {
$this->_options = json_decode( $this->_options );
}
// $this->_logger->info('get_option = ' . var_export($this->_options, true));
if ( false === $this->_options ) {
$this->clear();
}
$cached = false;
}
if ( ! WP_FS__DEBUG_SDK && ! $cached ) {
// Set non encoded cache.
wp_cache_set( $option_name, $this->_options, $cache_group );
}
}
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @return bool
*/
function is_loaded() {
return isset( $this->_options );
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @return bool
*/
function is_empty() {
return ( $this->is_loaded() && false === $this->_options );
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.6
*
* @param bool $flush
*/
function clear( $flush = false ) {
$this->_logger->entrance();
$this->_options = array();
if ( $flush ) {
$this->store();
}
}
/**
* Delete options manager from DB.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.9
*/
function delete() {
$option_name = $this->get_option_manager_name();
if ( $this->_is_network_storage ) {
delete_site_option( $option_name );
} else if ( $this->_blog_id > 0 ) {
delete_blog_option( $this->_blog_id, $option_name );
} else {
delete_option( $option_name );
}
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.6
*
* @param string $option
*
* @return bool
*/
function has_option( $option ) {
return array_key_exists( $option, $this->_options );
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @param string $option
* @param mixed $default
*
* @return mixed
*/
function get_option( $option, $default = null ) {
$this->_logger->entrance( 'option = ' . $option );
if ( ! $this->is_loaded() ) {
$this->load();
}
if ( is_array( $this->_options ) ) {
$value = isset( $this->_options[ $option ] ) ?
$this->_options[ $option ] :
$default;
} else if ( is_object( $this->_options ) ) {
$value = isset( $this->_options->{$option} ) ?
$this->_options->{$option} :
$default;
} else {
$value = $default;
}
/**
* If it's an object, return a clone of the object, otherwise,
* external changes of the object will actually change the value
* of the object in the options manager which may lead to an unexpected
* behaviour and data integrity when a store() call is triggered.
*
* Example:
* $object1 = $options->get_option( 'object1' );
* $object1->x = 123;
*
* $object2 = $options->get_option( 'object2' );
* $object2->y = 'dummy';
*
* $options->set_option( 'object2', $object2, true );
*
* If we don't return a clone of option 'object1', setting 'object2'
* will also store the updated value of 'object1' which is quite not
* an expected behaviour.
*
* @author Vova Feldman
*/
return is_object( $value ) ? clone $value : $value;
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @param string $option
* @param mixed $value
* @param bool $flush
*/
function set_option( $option, $value, $flush = false ) {
$this->_logger->entrance( 'option = ' . $option );
if ( ! $this->is_loaded() ) {
$this->clear();
}
/**
* If it's an object, store a clone of the object, otherwise,
* external changes of the object will actually change the value
* of the object in the options manager which may lead to an unexpected
* behaviour and data integrity when a store() call is triggered.
*
* Example:
* $object1 = new stdClass();
* $object1->x = 123;
*
* $options->set_option( 'object1', $object1 );
*
* $object1->x = 456;
*
* $options->set_option( 'object2', $object2, true );
*
* If we don't set the option as a clone of option 'object1', setting 'object2'
* will also store the updated value of 'object1' ($object1->x = 456 instead of
* $object1->x = 123) which is quite not an expected behaviour.
*
* @author Vova Feldman
*/
$copy = is_object( $value ) ? clone $value : $value;
if ( is_array( $this->_options ) ) {
$this->_options[ $option ] = $copy;
} else if ( is_object( $this->_options ) ) {
$this->_options->{$option} = $copy;
}
if ( $flush ) {
$this->store();
}
}
/**
* Unset option.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @param string $option
* @param bool $flush
*/
function unset_option( $option, $flush = false ) {
$this->_logger->entrance( 'option = ' . $option );
if ( is_array( $this->_options ) ) {
if ( ! isset( $this->_options[ $option ] ) ) {
return;
}
unset( $this->_options[ $option ] );
} else if ( is_object( $this->_options ) ) {
if ( ! isset( $this->_options->{$option} ) ) {
return;
}
unset( $this->_options->{$option} );
}
if ( $flush ) {
$this->store();
}
}
/**
* Dump options to database.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*/
function store() {
$this->_logger->entrance();
$option_name = $this->get_option_manager_name();
if ( $this->_logger->is_on() ) {
$this->_logger->info( $option_name . ' = ' . var_export( $this->_options, true ) );
}
// Update DB.
if ( $this->_is_network_storage ) {
update_site_option( $option_name, $this->_options );
} else if ( $this->_blog_id > 0 ) {
update_blog_option( $this->_blog_id, $option_name, $this->_options );
} else {
update_option( $option_name, $this->_options, $this->_autoload );
}
if ( ! WP_FS__DEBUG_SDK ) {
wp_cache_set( $option_name, $this->_options, $this->get_cache_group() );
}
}
/**
* Get options keys.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.3
*
* @return string[]
*/
function get_options_keys() {
if ( is_array( $this->_options ) ) {
return array_keys( $this->_options );
} else if ( is_object( $this->_options ) ) {
return array_keys( get_object_vars( $this->_options ) );
}
return array();
}
#--------------------------------------------------------------------------------
#region Migration
#--------------------------------------------------------------------------------
/**
* Migrate options from site level.
*
* @author Vova Feldman (@svovaf)
* @since 2.0.0
*/
function migrate_to_network() {
$site_options = FS_Option_Manager::get_manager($this->_id, true, false);
$options = is_object( $site_options->_options ) ?
get_object_vars( $site_options->_options ) :
$site_options->_options;
if ( ! empty( $options ) ) {
foreach ( $options as $key => $val ) {
$this->set_option( $key, $val, false );
}
$this->store();
}
}
#endregion
#--------------------------------------------------------------------------------
#region Helper Methods
#--------------------------------------------------------------------------------
/**
* @return string
*/
private function get_option_manager_name() {
return $this->_id;
}
/**
* @author Vova Feldman (@svovaf)
* @since 2.0.0
*
* @return string
*/
private function get_cache_group() {
$group = WP_FS__SLUG;
if ( $this->_is_network_storage ) {
$group .= '_ms';
} else if ( $this->_blog_id > 0 ) {
$group .= "_s{$this->_blog_id}";
}
return $group;
}
#endregion
}

View File

@ -0,0 +1,162 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 1.0.6
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class FS_Plan_Manager {
/**
* @var FS_Plan_Manager
*/
private static $_instance;
/**
* @return FS_Plan_Manager
*/
static function instance() {
if ( ! isset( self::$_instance ) ) {
self::$_instance = new FS_Plan_Manager();
}
return self::$_instance;
}
private function __construct() {
}
/**
* @param FS_Plugin_License[] $licenses
*
* @return bool
*/
function has_premium_license( $licenses ) {
if ( is_array( $licenses ) ) {
/**
* @var FS_Plugin_License[] $licenses
*/
foreach ( $licenses as $license ) {
if ( ! $license->is_utilized() && $license->is_features_enabled() ) {
return true;
}
}
}
return false;
}
/**
* Check if plugin has any paid plans.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param FS_Plugin_Plan[] $plans
*
* @return bool
*/
function has_paid_plan( $plans ) {
if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
return false;
}
/**
* @var FS_Plugin_Plan[] $plans
*/
for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
if ( ! $plans[ $i ]->is_free() ) {
return true;
}
}
return false;
}
/**
* Check if plugin has any free plan, or is it premium only.
*
* Note: If no plans configured, assume plugin is free.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.7
*
* @param FS_Plugin_Plan[] $plans
*
* @return bool
*/
function has_free_plan( $plans ) {
if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
return true;
}
/**
* @var FS_Plugin_Plan[] $plans
*/
for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
if ( $plans[ $i ]->is_free() ) {
return true;
}
}
return false;
}
/**
* Find all plans that have trial.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.9
*
* @param FS_Plugin_Plan[] $plans
*
* @return FS_Plugin_Plan[]
*/
function get_trial_plans( $plans ) {
$trial_plans = array();
if ( is_array( $plans ) && 0 < count( $plans ) ) {
/**
* @var FS_Plugin_Plan[] $plans
*/
for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
if ( $plans[ $i ]->has_trial() ) {
$trial_plans[] = $plans[ $i ];
}
}
}
return $trial_plans;
}
/**
* Check if plugin has any trial plan.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.9
*
* @param FS_Plugin_Plan[] $plans
*
* @return bool
*/
function has_trial_plan( $plans ) {
if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
return true;
}
/**
* @var FS_Plugin_Plan[] $plans
*/
for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
if ( $plans[ $i ]->has_trial() ) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,220 @@
<?php
/**
* @package Freemius
* @copyright Copyright (c) 2015, Freemius, Inc.
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
* @since 1.0.6
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class FS_Plugin_Manager {
/**
* @since 1.2.2
*
* @var string|number
*/
protected $_module_id;
/**
* @since 1.2.2
*
* @var FS_Plugin
*/
protected $_module;
/**
* @var FS_Plugin_Manager[]
*/
private static $_instances = array();
/**
* @var FS_Logger
*/
protected $_logger;
/**
* Option names
*
* @author Leo Fajardo (@leorw)
* @since 1.2.2
*/
const OPTION_NAME_PLUGINS = 'plugins';
const OPTION_NAME_THEMES = 'themes';
/**
* @param string|number $module_id
*
* @return FS_Plugin_Manager
*/
static function instance( $module_id ) {
$key = 'm_' . $module_id;
if ( ! isset( self::$_instances[ $key ] ) ) {
self::$_instances[ $key ] = new FS_Plugin_Manager( $module_id );
}
return self::$_instances[ $key ];
}
/**
* @param string|number $module_id
*/
protected function __construct( $module_id ) {
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_' . 'plugins', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
$this->_module_id = $module_id;
$this->load();
}
protected function get_option_manager() {
return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true, true );
}
/**
* @author Leo Fajardo (@leorw)
* @since 1.2.2
*
* @param string|bool $module_type "plugin", "theme", or "false" for all modules.
*
* @return array
*/
protected function get_all_modules( $module_type = false ) {
$option_manager = $this->get_option_manager();
if ( false !== $module_type ) {
return fs_get_entities( $option_manager->get_option( $module_type . 's', array() ), FS_Plugin::get_class_name() );
}
return array(
self::OPTION_NAME_PLUGINS => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_PLUGINS, array() ), FS_Plugin::get_class_name() ),
self::OPTION_NAME_THEMES => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_THEMES, array() ), FS_Plugin::get_class_name() ),
);
}
/**
* Load plugin data from local DB.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.6
*/
function load() {
$all_modules = $this->get_all_modules();
if ( ! is_numeric( $this->_module_id ) ) {
unset( $all_modules[ self::OPTION_NAME_THEMES ] );
}
foreach ( $all_modules as $modules ) {
/**
* @since 1.2.2
*
* @var $modules FS_Plugin[]
*/
foreach ( $modules as $module ) {
$found_module = false;
/**
* If module ID is not numeric, it must be a plugin's slug.
*
* @author Leo Fajardo (@leorw)
* @since 1.2.2
*/
if ( ! is_numeric( $this->_module_id ) ) {
if ( $this->_module_id === $module->slug ) {
$this->_module_id = $module->id;
$found_module = true;
}
} else if ( $this->_module_id == $module->id ) {
$found_module = true;
}
if ( $found_module ) {
$this->_module = $module;
break;
}
}
}
}
/**
* Store plugin on local DB.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.6
*
* @param bool|FS_Plugin $module
* @param bool $flush
*
* @return bool|\FS_Plugin
*/
function store( $module = false, $flush = true ) {
if ( false !== $module ) {
$this->_module = $module;
}
$all_modules = $this->get_all_modules( $this->_module->type );
$all_modules[ $this->_module->slug ] = $this->_module;
$options_manager = $this->get_option_manager();
$options_manager->set_option( $this->_module->type . 's', $all_modules, $flush );
return $this->_module;
}
/**
* Update local plugin data if different.
*
* @author Vova Feldman (@svovaf)
* @since 1.0.6
*
* @param \FS_Plugin $plugin
* @param bool $store
*
* @return bool True if plugin was updated.
*/
function update( FS_Plugin $plugin, $store = true ) {
if ( ! ($this->_module instanceof FS_Plugin ) ||
$this->_module->slug != $plugin->slug ||
$this->_module->public_key != $plugin->public_key ||
$this->_module->secret_key != $plugin->secret_key ||
$this->_module->parent_plugin_id != $plugin->parent_plugin_id ||
$this->_module->title != $plugin->title
) {
$this->store( $plugin, $store );
return true;
}
return false;
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.6
*
* @param FS_Plugin $plugin
* @param bool $store
*/
function set( FS_Plugin $plugin, $store = false ) {
$this->_module = $plugin;
if ( $store ) {
$this->store();
}
}
/**
* @author Vova Feldman (@svovaf)
* @since 1.0.6
*
* @return bool|\FS_Plugin
*/
function get() {
return isset( $this->_module ) ?
$this->_module :
false;
}
}

View File

@ -0,0 +1,3 @@
<?php
// Silence is golden.
// Hide file structure from users on unprotected servers.