can_install = charitable_can_install( 'addon' );
do_action( 'charitable_addons_directory_settings_start', $this );
add_action( 'admin_enqueue_scripts', array( $this, 'charitable_admin_scripts' ) );
// Add callbacks for content.
add_action( 'charitable_addons_directory_section', array( $this, 'addons_directory_content' ) );
}
/**
* Add JS text scripts and load admin scripts.
*
* @since 1.8.0
* @version 1.8.1.15 - Added upgrade_modal.
* @version 1.8.5.3 - Added is_pro_installed.
* @version 1.8.6.2 - Added filter for strings.
*/
public function charitable_admin_scripts() {
$min = charitable_get_min_suffix();
$version = charitable()->get_version();
$assets_dir = charitable()->get_path( 'assets', false );
$strings = apply_filters(
'charitable_admin_strings',
array(
'nonce' => wp_create_nonce( 'charitable-admin' ),
'addon_activate' => esc_html__( 'Activate', 'charitable' ),
'addon_activated' => esc_html__( 'Activated', 'charitable' ),
'addon_active' => esc_html__( 'Active', 'charitable' ),
'addon_deactivate' => esc_html__( 'Deactivate', 'charitable' ),
'addon_inactive' => esc_html__( 'Inactive', 'charitable' ),
'addon_install' => esc_html__( 'Install Addon', 'charitable' ),
'addon_error' => sprintf(
wp_kses( /* translators: %1$s - An addon download URL, %2$s - Link to manual installation guide. */
__( 'Could not install the addon. Please download it from wpcharitable.com and install it manually.', 'charitable' ),
array(
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
),
)
),
'https://wpcharitable.com/account/licenses/',
'https://wpcharitable.com/docs/how-to-manually-install-addons-in-charitable/'
),
'plugin_error' => esc_html__( 'Could not install the plugin automatically. Please download and install it manually.', 'charitable' ),
'addon_search' => esc_html__( 'Searching Addons', 'charitable' ),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'admin_url' => admin_url(),
'ok' => esc_html__( 'OK', 'charitable' ),
'oops' => esc_html__( 'Oops!', 'charitable' ),
'please_enter_key' => esc_html__( 'Please enter a valid license key.', 'charitable' ),
'manual_upgrade' => esc_html__( 'Manual Upgrade', 'charitable' ),
'cancel' => esc_html__( 'Cancel', 'charitable' ),
'close' => esc_html__( 'Close', 'charitable' ),
'plugin_install_activate_btn' => esc_html__( 'Install and Activate', 'charitable' ),
'plugin_install_activate_confirm' => esc_html__( 'needs to be installed and activated to import its forms. Would you like us to install and activate it for you?', 'charitable' ),
'plugin_activate_btn' => esc_html__( 'Activate', 'charitable' ),
'plugin_activate_confirm' => esc_html__( 'needs to be activated to import its forms. Would you like us to activate it for you?', 'charitable' ),
'connecting' => esc_html__( 'Connecting...', 'charitable' ),
'save_refresh' => esc_html__( 'Save and Refresh', 'charitable' ),
'server_error' => esc_html__( 'Unfortunately there was a server connection error.', 'charitable' ),
'testing' => esc_html__( 'Testing', 'charitable' ),
'upgrade_completed' => esc_html__( 'Upgrade was successfully completed!', 'charitable' ),
'edit_license' => esc_html__( 'To edit the License Key, please first click the Deactivate Key button. Please note that deactivating this key will remove access to updates, addons, and support.', 'charitable' ),
'something_went_wrong' => esc_html__( 'Something went wrong', 'charitable' ),
'success' => esc_html__( 'Success', 'charitable' ),
'loading' => esc_html__( 'Loading...', 'charitable' ),
'use_simple_contact_form' => esc_html__( 'Use Simple Contact Form Template', 'charitable' ),
'error_select_template' => esc_html__( 'Something went wrong while applying the template.', 'charitable' ),
'plugin_asset_dir' => charitable()->get_path( 'assets', false ),
'install' => esc_html__( 'Install', 'charitable' ),
'activate' => esc_html__( 'Activate', 'charitable' ),
'setup' => esc_html__( 'Setup', 'charitable' ),
'settings' => esc_html__( 'Settings', 'charitable' ),
'thanks_for_interest' => esc_html__( 'Thanks for your interest in Charitable Pro!', 'charitable' ),
'upgrade_modal' => charitable_get_upgrade_modal_text(),
'autoshow_plugin_notifications' => charitable_get_autoshow_plugin_notifications(),
'is_pro_installed' => (int) charitable_is_pro_installed(),
'activated_title' => esc_html__( 'Almost Done', 'charitable' ),
'activated_content' => esc_html__( 'Charitable Pro is installed but not activated.', 'charitable' ),
)
);
wp_enqueue_script(
'listjs',
$assets_dir . 'js/libraries/list.min.js',
array( 'jquery' ),
$version,
false
);
wp_register_style(
'charitable-admin-addons-directory',
$assets_dir . 'css/admin/charitable-admin-addons-directory' . $min . '.css',
array(),
$version
);
wp_enqueue_style( 'charitable-admin-addons-directory' );
wp_register_script(
'charitable-admin-addon-directory',
$assets_dir . 'js/admin/charitable-admin-addon-directory' . $min . '.js',
array( 'jquery' ),
$version,
true
);
wp_enqueue_script( 'charitable-admin-addon-directory' );
wp_localize_script(
'charitable-admin-addon-directory',
'charitable_admin',
$strings
);
}
/**
* Returns and/or create the single instance of this class.
*
* @since 1.2.0
*
* @return Charitable_Addons_Directory
*/
public static function get_instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Attemps to confirm if the current license is a legacy one, even if we have a plan_id / slug (which normally came from newer license)
*
* @since 1.7.0.4
*
* @return Charitable_Addons_Directory
*/
public static function is_current_plan_legacy() {
$settings = get_option( 'charitable_settings' );
// always check the v2 settings first.
$plan_id = isset( $settings['licenses']['charitable-v2']['plan_id'] ) ? intval( $settings['licenses']['charitable-v2']['plan_id'] ) : false;
$valid = isset( $settings['licenses']['charitable-v2']['valid'] ) ? trim( esc_html( $settings['licenses']['charitable-v2']['valid'] ) ) : false;
if ( false === $valid || '' === $valid ) {
// try looking at the "legacy" license for a plan_id.
if ( isset( $settings['licenses']['charitable'] ) ) {
$plan_id = isset( $settings['licenses']['charitable']['plan_id'] ) ? intval( $settings['licenses']['charitable']['plan_id'] ) : false;
$valid = isset( $settings['licenses']['charitable']['valid'] ) ? trim( esc_html( $settings['licenses']['charitable']['valid'] ) ) : false;
if ( $plan_id ) {
return true;
}
}
// try looking for an install that just installed an addon license only, which would be another example of a "legacy" license.
if ( ! empty( $settings['licenses'] ) ) {
$addon_licenses = $settings['licenses'];
if ( isset( $addon_licenses['charitable'] ) ) {
unset( $addon_licenses['charitable'] );
}
if ( isset( $addon_licenses['charitable_pro'] ) ) {
unset( $addon_licenses['charitable_pro'] );
}
foreach ( $addon_licenses as $addon_license ) {
if ( isset( $addon_license['valid'] ) && 1 === intval( $addon_license['valid'] ) ) {
return true;
}
}
}
}
return false;
}
/**
* Returns and/or create the single instance of this class.
*
* @since 1.2.0
*
* @return Charitable_Addons_Directory
*/
public static function get_current_plan_slug() {
$settings = get_option( 'charitable_settings' );
// always check the v2 settings first.
$plan_id = isset( $settings['licenses']['charitable-v2']['plan_id'] ) ? intval( $settings['licenses']['charitable-v2']['plan_id'] ) : false;
$valid = isset( $settings['licenses']['charitable-v2']['valid'] ) ? trim( esc_html( $settings['licenses']['charitable-v2']['valid'] ) ) : false;
if ( false === $valid || '' === $valid ) {
// try looking at the "legacy" license for a plan_id.
if ( isset( $settings['licenses']['charitable'] ) ) {
$plan_id = isset( $settings['licenses']['charitable']['plan_id'] ) ? intval( $settings['licenses']['charitable']['plan_id'] ) : false;
$valid = isset( $settings['licenses']['charitable']['valid'] ) ? trim( esc_html( $settings['licenses']['charitable']['valid'] ) ) : false;
}
}
if ( $valid ) {
switch ( $plan_id ) {
case 1:
$plan_slug = 'basic';
break;
case 2:
$plan_slug = 'plus';
break;
case 3:
$plan_slug = 'pro';
break;
case 4:
$plan_slug = 'agency';
break;
default:
$plan_slug = 'lite';
break;
}
} else {
$plan_slug = 'lite';
}
return $plan_slug;
}
/**
* Callback for displaying the UI for Addons.
*
* @since 1.7.0
* @version 1.8.5.1
*/
public function addons_directory_content() {
// Get Addons.
$temp_addons = $this->get_addons();
// If error(s) occured during license key verification, display them and exit now.
if ( empty( $temp_addons ) ) { ?>
$addon ) {
if ( 'charitable-pro-plugin' === $addon['slug'] ) {
// Remove from current location.
unset( $temp_addons[ $addon_type ][ $i ] );
// Add to start of recommended array.
if ( ! isset( $temp_addons['recommended'] ) ) {
$temp_addons['recommended'] = array();
}
array_unshift( $temp_addons['recommended'], $addon );
}
}
}
}
}
$recommended_addons = array_map( array( $this, 'prepare_addon_data' ), ! empty( $temp_addons['recommended'] ) ? $temp_addons['recommended'] : array() );
$unlicensed_addons = array_map( array( $this, 'prepare_addon_data' ), ! empty( $temp_addons['unlicensed'] ) ? $temp_addons['unlicensed'] : array() );
$licensed_addons = array_map( array( $this, 'prepare_addon_data' ), ! empty( $temp_addons['licensed'] ) ? $temp_addons['licensed'] : array() );
$addons = array(
'recommended' => $recommended_addons,
'licensed' => $licensed_addons,
'unlicensed' => $unlicensed_addons,
);
// If no Addon(s) were returned, our API call returned an error.
// Show an error message with a button to reload the page, which will trigger another API call.
if ( ! $addons ) {
?>
charitable_get_upgrade_link();
$type = ( isset( $_GET['plan'] ) ) ? esc_attr( $_GET['plan'] ) : $this->get_current_plan_slug(); // phpcs:ignore
$preset_search = ( isset( $_GET['search'] ) ) ? trim( esc_attr( $_GET['search'] ) ) : ''; // phpcs:ignore
?>
$addon ) {
$this->get_addon_card( $addon, $i, true, $installed_plugins );
}
}
?>
$addon ) {
$this->get_addon_card( $addon, $i, true, $installed_plugins );
}
}
?>
$addon ) {
$this->get_addon_card( $addon, $i, false, $installed_plugins );
}
}
?>
get_current_plan_slug();
$addons = get_transient( '_charitable_addons' ); // @codingStandardsIgnoreLine - testing.
// Get addons data from transient or perform API query if no transient.
if ( false === $addons ) {
$addons = $this->get_addons_data_from_server( $key );
}
// If no Addons exist, return false.
if ( ! $addons ) {
return array();
}
// Iterate through Addons, to build two arrays:
// - Addons the user is licensed to use,
// - Addons the user isn't licensed to use.
$results = array(
'recommended' => array(),
'licensed' => array(),
'unlicensed' => array(),
);
foreach ( (array) $addons as $i => $addon ) {
if ( ! isset( $addon['slug'] ) || $addon['slug'] === '' || strtolower( $addon['slug'] ) === 'auto draft' ) {
continue;
}
// Filter out the Square addon since it's now built into the core plugin.
if ( 'charitable-square' === $addon['slug'] ) {
continue;
}
if ( ! is_array( $addon ) ) {
$addon = array();
}
$addon['path'] = ( '' === $addon['path'] ) ? sprintf( '%1$s/%1$s.php', $addon['slug'] ) : $addon['path'];
$type = ( isset( $_GET['license'] ) ) ? esc_html( $_GET['license'] ) : $this->get_current_plan_slug(); // phpcs:ignore
if ( isset( $addon['featured'] ) && in_array( 'recommended', $addon['featured'] ) ) {
$results['recommended'][] = (array) $addon;
continue;
} else { // phpcs:ignore
// Determine if the addon belongs licensed or unlicensed based on the confirmed plan.
if ( isset( $addon['license'] ) && in_array( $type, $addon['license'] ) ) {
$results['licensed'][] = (array) $addon;
continue;
} else {
$results['unlicensed'][] = (array) $addon;
continue;
}
}
}
// Return Addons, split by licensed and unlicensed.
return $results;
}
/**
* Stores an upgrade link used for the addons page when a person needs a pro license.
*
* @since 1.7.0.4
*
* @return string The url
*/
public function charitable_get_upgrade_link() {
return 'https://wpcharitable.com/lite-upgrade/?discount=LITEUPGRADE&utm_source=WordPress&utm_campaign=liteplugin&utm_medium=addons&utm_content=ActiveCampaign%20Addon';
}
/**
* Retrieve the plugin basename from the plugin slug.
*
* @since 1.7.0
*
* @param string $slug The plugin slug.
* @return string The plugin basename if found, else the plugin slug.
*/
public function get_plugin_basename_from_slug( $slug ) {
$keys = array_keys( get_plugins() );
foreach ( $keys as $key ) {
if ( preg_match( '|^' . $slug . '|', $key ) ) {
return $key;
}
}
return $slug;
}
/**
* Outputs the addon "box" on the addons page.
*
* @since 1.7.0
* @version 1.8.1.12 - Added EDD to replacements string check.
*
* @param object $addon Addon data from the API / transient call.
* @param int $counter Index of this Addon in the collection.
* @param bool $is_licensed Whether the Addon is licensed for use.
* @param array $installed_plugins Installed WordPress Plugins.
*/
public function get_addon_card( $addon, $counter = 0, $is_licensed = false, $installed_plugins = false ) {
if ( ! isset( $addon['slug'] ) ) {
return;
}
// Setup some vars.
$plugin_basename = $this->get_plugin_basename_from_slug( $addon['slug'] );
$replacements = array(
'charitable-recurring-donations' => 'charitable-recurring',
'charitable-anonymous-donations' => 'charitable-anonymous',
'charitable-easy-digital-downloads-connect' => 'charitable-edd',
);
if ( array_key_exists( $addon['slug'], $replacements ) ) {
$plugin_basename = $this->get_plugin_basename_from_slug( $replacements[ $addon['slug'] ] );
}
if ( ! $installed_plugins ) {
$installed_plugins = get_plugins();
}
// If the Addon doesn't supply an upgrade_url key, it's because the user hasn't provided a license.
// get_upgrade_link() will return the Lite or Pro link as necessary for us.
if ( ! isset( $addon['upgrade_url'] ) ) {
$addon['upgrade_url'] = $this->charitable_get_upgrade_link();
}
// Add marketing tracking.
$addon['upgrade_url'] = add_query_arg(
array(
'utm_source' => 'proplugin',
'utm_medium' => 'addonspage',
'utm_campaign' => str_replace( '-', '', $addon['slug'] ) . 'addon',
),
$addon['upgrade_url']
);
if ( ! isset( $installed_plugins[ $plugin_basename ] ) ) {
switch ( $this->get_current_plan_slug() ) {
case 'basic':
$the_plans = array( 'basic' );
break;
case 'plus':
$the_plans = array( 'basic', 'plus' );
break;
case 'pro':
$the_plans = array( 'basic', 'plus', 'pro' );
break;
case 'agency':
$the_plans = array( 'basic', 'plus', 'pro', 'agency', 'elite' );
break;
default:
$the_plans = array();
break;
}
$addon['status'] = 'missing';
$addon['action'] = 'license';
foreach ( $addon['license'] as $license_to_check ) {
// Check if $license_to_check is a vaule in the $the_plans array.
if ( in_array( $license_to_check, $the_plans ) ) {
$addon['status'] = 'missing';
$addon['action'] = 'install';
}
}
} else {
// Plugin is installed.
if ( is_plugin_active( $plugin_basename ) ) {
$addon['status'] = 'active';
$addon['action'] = 'deactivate';
} else {
$addon['status'] = 'installed';
$addon['action'] = 'activate';
}
}
if ( 'charitable-pro-plugin' === $addon['slug'] ) {
// Check if Charitable Pro is installed.
$charitable_pro_basename = 'charitable-pro/charitable.php';
$is_pro_installed = isset( $installed_plugins[ $charitable_pro_basename ] );
$is_pro_active = is_plugin_active( $charitable_pro_basename );
if ( $is_pro_installed && ! $is_pro_active ) {
// Plugin is installed but not activated - show activate button.
$button = '
';
} else {
// Plugin is not installed or is active - show download link.
$button = '
' . esc_html__( 'Download Pro', 'charitable' ) . '';
}
} else {
$button = $this->get_addon_button_html( $addon );
}
$status_label = $this->get_addon_status_label( $addon['status'] );
// get the icon/graphic.
$icon = isset( $addon['icon'] ) ? esc_url( $addon['icon'] ) : 'placeholder';
// get the plugin description.
$sections = unserialize( $addon['sections'] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
$description = wp_strip_all_tags( ( $sections['description'] ) );
$is_recommended = ( isset( $addon['featured'] ) && is_array( $addon['featured'] ) && in_array( 'recommended', $addon['featured'] ) ) ? true : false;
// css.
$css = array( 'addon-container' );
if ( $is_recommended || 'charitable-pro-plugin' === $addon['slug'] ) {
$css[] = 'recommended';
}
// Output the card.
$allowed_html = array(
'a' => array(
'href' => array(),
'title' => array(),
),
'br' => array(),
'em' => array(),
'strong' => array(),
'p' => array(),
);
?>
' . wp_kses_post( $status_label ) . ''
);
?>
get_license_key_for_upgrade();
if ( false !== $license_data_to_send && isset( $license_data_to_send['license'] ) ) {
$upid = 0; // the upgrade plan id.
if ( in_array( 'agency', $addon['license'] ) ) { // if the desired addon is in the agency category license, we need to pass along the upgrade plan id of 4.
$upid = 4;
}
if ( in_array( 'pro', $addon['license'] ) ) { // if the desired addon is in the pro category license, we need to pass along the upgrade plan id of 3.
$upid = 3;
}
if ( in_array( 'plus', $addon['license'] ) ) { // if the desired addon is in the plus category license, we need to pass along the upgrade plan id of 2.
$upid = 2;
}
// we have a valid license that we could get an upgrade path, etc. from.
$upgrade_url = charitable_ga_url(
// pid = plan_id.
self::UPDATE_URL . '/?license_upgrade=true&license_key=' . $license_data_to_send['license'] . '&upid=' . $upid . '&pid=' . $license_data_to_send['plan_id'] . '&addon=' . esc_html( $addon['name'] ),
urlencode( 'Plugin Addon Page' ), // phpcs:ignore
urlencode( $addon['name'] . ' Upgrade' ) // phpcs:ignore
);
} else {
// they may be on 'pro' mode but they don't have a license we can send, so fall back to sending them to the pricing page.
$upgrade_url = charitable_ga_url(
'https://www.wpcharitable.com/pricing/',
urlencode( 'Plugin Addon Page' ), // phpcs:ignore
urlencode( $addon['name'] . ' Upgrade' ) // phpcs:ignore
);
}
} else {
// they are on lite, with no license so send them to the pricing page.
$upgrade_url = charitable_ga_url(
'https://www.wpcharitable.com/pricing/',
urlencode( 'Plugin Addon Page' ), // phpcs:ignore
urlencode( $addon['name'] . ' Upgrade' ) // phpcs:ignore
);
}
return sprintf(
'
%2$s',
$upgrade_url,
esc_html__( 'Upgrade Now', 'charitable' )
);
}
$html = '';
// Override to account for odd setup prior to new ownership.
$addon['path'] = str_replace( 'charitable-recurring-donations', 'charitable-recurring', $addon['path'] );
// Apply filter, mostly for testing and troubleshooting.
$addon = apply_filters( 'get_addon_button_html_addon', $addon );
switch ( $addon['status'] ) {
case 'active':
$html = '
';
break;
case 'installed':
$html = '
';
break;
case 'missing':
if ( ! $this->can_install ) {
break;
}
// Testing backup links for download.
$download_url = ! empty( $addon['download_link'] ) ? esc_url( $addon['download_link'] ) : false;
$download_url = ( false === $download_url ) && ! empty( $addon['install'] ) ? esc_url( $addon['install'] ) : $download_url;
$html = '
';
break;
}
return $html;
}
/**
* Get addon status label.
*
* @since 1.6.7
*
* @param string $status Addon status.
*
* @return string
*/
private function get_addon_status_label( $status ) {
$label = array(
'active' => esc_html__( 'Active', 'charitable' ),
'installed' => esc_html__( 'Inactive', 'charitable' ),
'missing' => esc_html__( 'Not Installed', 'charitable' ),
);
return isset( $label[ $status ] ) ? $label[ $status ] : '';
}
/**
* Prepare addon data.
*
* @since 1.6.6
*
* @param array $addon Addon data.
*
* @return array Extended addon data.
*/
protected function prepare_addon_data( $addon ) {
if ( empty( $addon ) ) {
return array();
}
$addon['title'] = ! empty( $addon['title'] ) ? $addon['title'] : '';
$addon['slug'] = ! empty( $addon['slug'] ) ? $addon['slug'] : '';
/* translators: %s - addon name. */
$addon['modal_name'] = sprintf( esc_html__( '%s addon', 'charitable' ), $addon['name'] );
$addon['clear_slug'] = str_replace( 'charitable-', '', $addon['slug'] );
$addon['utm_content'] = ucwords( str_replace( '-', ' ', $addon['clear_slug'] ) );
$addon['license'] = empty( $addon['license'] ) ? array() : (array) $addon['license'];
$addon['license_level'] = $this->get_license_level( $addon );
$addon['icon'] = ! empty( $addon['icon'] ) ? $addon['icon'] : '';
$addon['path'] = sprintf( '%1$s/%1$s.php', $addon['slug'] );
$addon['video'] = ! empty( $addon['video'] ) ? $addon['video'] : '';
$addon['plugin_allow'] = $this->has_access( $addon );
$addon['status'] = 'missing';
$addon['action'] = 'upgrade';
$addon['page_url'] = empty( $addon['url'] ) ? '' : $addon['url'];
$addon['doc_url'] = empty( $addon['doc'] ) ? '' : $addon['doc'];
$addon['url'] = '';
static $nonce = '';
$nonce = empty( $nonce ) ? wp_create_nonce( 'charitable-admin' ) : $nonce;
$addon['nonce'] = $nonce;
return $addon;
}
/**
* Get available addon data by slug.
*
* @since 1.6.6
*
* @param string $slug Addon slug, can be both "charitable-drip" and "drip".
*
* @return array Single addon data. Empty array if addon is not found.
*/
public function get_addon( $slug ) {
$slug = 'charitable-' . str_replace( 'charitable-', '', sanitize_key( $slug ) );
$addon = ! empty( $this->available_addons[ $slug ] ) ? $this->available_addons[ $slug ] : array();
// In case if addon is "not available" let's try to get and prepare addon data from all addons.
if ( empty( $addon ) ) {
$addon = ! empty( $this->addons[ $slug ] ) ? $this->prepare_addon_data( $this->addons[ $slug ] ) : array();
}
return $addon;
}
/**
* Get license level of the addon.
*
* @since 1.6.6
*
* @param array|string $addon Addon data array OR addon slug.
*
* @return string License level: pro | elite.
*/
private function get_license_level( $addon ) {
if ( empty( $addon ) ) {
return '';
}
$addon = is_string( $addon ) ? $this->get_addon( $addon ) : $addon;
$addon['license'] = empty( $addon['license'] ) ? array() : (array) $addon['license'];
// TODO: convert to a class constant when we will drop PHP 5.5.
$levels = array( 'basic', 'plus', 'pro', 'elite', 'agency', 'ultimate' );
$license = '';
foreach ( $levels as $level ) {
if ( in_array( $level, $addon['license'], true ) ) {
$license = $level;
break;
}
}
if ( empty( $license ) ) {
return '';
}
return in_array( $license, array( 'basic', 'plus', 'pro' ), true ) ? 'pro' : 'elite';
}
/**
* This attempts to send the BEST license key to Charitable whenever a suggested upgrade is possible.
* Note: We look for "v2" licenses in settings first, then attempt to locate a charitable basic/plus/pro/agency (new or oldschool) license.
* We don't return individual addons in this function because there's no good way to suggest an upgrade EDD license path.
*
* @since 1.7.0.4
*
* @return array
*/
private function get_license_key_for_upgrade() {
$settings = get_option( 'charitable_settings' );
if ( isset( $settings['licenses']['charitable-v2']['license'] ) && ! empty( $settings['licenses']['charitable-v2']['license'] ) ) {
// attempt to grab the "v2" license.
$license = esc_html( $settings['licenses']['charitable-v2']['license'] );
$plan_id = isset( $settings['licenses']['charitable-v2']['plan_id'] ) && ! empty( $settings['licenses']['charitable-v2']['plan_id'] ) ? intval( $settings['licenses']['charitable-v2']['plan_id'] ) : false;
return array(
'license' => $license,
'plan_id' => $plan_id,
);
} elseif ( isset( $settings['licenses']['charitable'] ) && ! empty( $settings['licenses']['charitable'] ) ) {
$license = esc_html( $settings['licenses']['charitable'] );
return array(
'license' => $license,
'plan_id' => false,
); // no plan id when this was added into settings.
} elseif ( isset( $settings['licenses']['charitable_pro'] ) && ! empty( $settings['licenses']['charitable_pro'] ) ) {
// unlikely but technically possible.
$license = esc_html( $settings['licenses']['charitable_pro'] );
return array(
'license' => $license,
'plan_id' => false,
); // no plan id when this was added into settings.
}
return false;
}
/**
* Determine if user's license level has access.
*
* @since 1.6.6
*
* @param array|string $addon Addon data array OR addon slug.
*
* @return bool
*/
protected function has_access( $addon ) { // phpcs:ignore
return true;
}
/**
* Return the latest versions of Charitable plugins.
*
* @since 1.7.0.4
*
* @param array $licenses Array of license keys.
*
* @return array
*/
public function get_addons_data_from_server( $licenses = false ) {
$versions = get_transient( '_charitable_plugin_versions' );
if ( false === $versions ) {
if ( false === $licenses ) {
$licenses = array();
foreach ( $this->get_licenses() as $license ) {
if ( isset( $license['license'] ) ) {
$licenses[] = $license['license'];
}
}
}
$response = wp_remote_post(
self::UPDATE_URL . '/edd-api/versions-v3/',
array(
'sslverify' => false,
'timeout' => 15,
'body' => array(
'licenses' => $licenses,
'url' => home_url(),
),
)
);
$response_body = wp_remote_retrieve_body( $response );
$response_code = wp_remote_retrieve_response_code( $response );
// Bail out early if there are any errors.
if ( (int) $response_code !== 200 || is_wp_error( $response_body ) ) {
return false;
}
$versions = json_decode( $response_body, true );
set_transient( '_charitable_plugin_versions', $versions, DAY_IN_SECONDS );
} // end if
return $versions;
}
/**
* Return the latest versions of Charitable plugins.
*
* @since 1.4.0
* @since 1.8.1.1 - Removed cache_get method and site a site option instead.
* @since 1.8.1.3 - Added request_recently_failed check and log_failed_request call.
*
* @return array
*/
protected function get_versions() {
$versions = get_site_option( 'wpc_plugin_versions' );
if ( false === $versions || ! isset( $versions['expires'] ) || time() > $versions['expires'] || ! isset( $versions['data'] ) ) {
/*
* Check and see if this request recently failed.
* Most likely because the site is down or didn't return a 200 response.
* Regardless this means we're allowed to try again for a while.
*/
if ( $this->request_recently_failed() ) {
if ( charitable_is_debug() ) {
// phpcs:disable
error_log( 'WPCharitable Debug Error: get_versions (licenses) API call aborted because it recently failed' );
// phpcs:enable
}
return false;
}
$licenses = array();
foreach ( $this->get_licenses() as $license ) {
if ( isset( $license['license'] ) ) {
$licenses[] = $license['license'];
}
}
$response = wp_remote_post(
self::UPDATE_URL . '/edd-api/versions-v2/',
array(
'sslverify' => false,
'timeout' => 15,
'body' => array(
'licenses' => $licenses,
'url' => home_url(),
),
)
);
// check the response code... if it's bad, then return what we have if anything.
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
// Log this failure so we don't keep trying.
$this->log_failed_request();
if ( charitable_is_debug() ) {
// phpcs:disable
error_log( 'WPCharitable Debug Error: get_versions (licenses) API call failed' );
error_log( print_r( $response, true ) );
// phpcs:enable
}
return ( false !== $versions && isset( $versions['data'] ) ) ? $versions['data'] : array();
}
$versions = wp_remote_retrieve_body( $response );
$versions = json_decode( $versions, true );
update_site_option(
'wpc_plugin_versions',
array(
'expires' => strtotime( '+1 hour', time() ),
'data' => $versions,
)
);
if ( charitable_is_debug() ) {
error_log( 'WPCharitable Debug Notice: get_versions (licenses) API call in addons directory successful and added to site option.' ); // @codingStandardsIgnoreLine
error_log( // @codingStandardsIgnoreLine
print_r( // @codingStandardsIgnoreLine
array(
'expires' => strtotime( '+1 hour', time() ),
'data' => $versions,
),
true
)
);
}
} else {
$versions = $versions['data'];
}
return $versions;
}
/**
* Logs a failed HTTP request for this API URL.
*
* We set a timestamp for 1 hour from now. This prevents future API requests from being
* made to this domain for 1 hour. Once the timestamp is in the past, API requests
* will be allowed again. This way if the site is down for some reason we don't bombard
* it with failed API requests.
*
* @see EDD_SL_Plugin_Updater::request_recently_failed
*
* @since 1.8.1.3
*/
private function log_failed_request() {
update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
}
/**
* Determines if a request has recently failed.
*
* @since 1.8.1.3
*
* @return bool
*/
private function request_recently_failed() {
$failed_request_details = get_option( $this->failed_request_cache_key );
// Request has never failed.
if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
return false;
}
/*
* Request previously failed, but the timeout has expired.
* This means we're allowed to try again.
*/
if ( time() > $failed_request_details ) {
delete_option( $this->failed_request_cache_key );
return false;
}
return true;
}
/**
* Return the list of licenses.
*
* Note: The licenses are not necessarily valid. If a user enters an invalid
* license, the license will be stored but it will be flagged as invalid.
*
* @since 1.0.0
*
* @return array[]
*/
public function get_licenses() {
if ( ! isset( $this->licenses ) ) {
$this->licenses = charitable_get_option( 'licenses', array() );
}
return $this->licenses;
}
}
endif;