42 if ( is_null( self::$_instance ) ) {
43 self::$_instance =
new self();
46 return self::$_instance;
63 add_filter(
'gk/foundation/ajax/' .
Framework::AJAX_ROUTER .
'/routes', [ $this,
'configure_ajax_routes' ] );
65 add_filter(
'plugin_action_links', [ $this,
'display_product_action_links' ], 10, 2 );
84 return array_merge( $routes, [
85 'activate_product' => [ $this,
'ajax_activate_product' ],
86 'deactivate_product' => [ $this,
'ajax_deactivate_product' ],
87 'install_product' => [ $this,
'ajax_install_product' ],
88 'update_product' => [ $this,
'ajax_update_product' ],
89 'get_products' => [ $this,
'ajax_get_products_data' ],
105 $payload = wp_parse_args( $payload, [
112 throw new Exception( esc_html__(
'You do not have a permission to perform this action.',
'gk-gravityview' ) );
130 if ( ! file_exists( ABSPATH .
'wp-admin/includes/class-wp-upgrader.php' ) ) {
131 throw new Exception( esc_html__(
'Unable to load core WordPress files required to install the product.',
'gk-gravityview' ) );
134 include_once( ABSPATH .
'wp-admin/includes/class-wp-upgrader.php' );
136 $product_id = $product[
'id'];
138 $product_download_link = null;
142 if ( empty( $product[
'download'] ) ) {
143 $licenses_data = $license_manager->get_licenses_data();
145 foreach ( $licenses_data as $key => $license_data ) {
146 if ( $license_manager->is_expired_license( $license_data[
'expiry'] ) || empty( $license_data[
'products'] ) || ! isset( $license_data[
'products'][ $product_id ] ) ) {
151 $license = $license_manager->check_license( $key );
153 LoggerFramework::get_instance()->warning(
"Unable to verify license key ${key} when installing product ID ${product_id}: " . $e->getMessage() );
158 if ( empty( $license[
'products'][ $product_id ][
'download'] ) ) {
162 $product_download_link = $license[
'products'][ $product_id ][
'download'];
168 if ( ! $product_download_link ) {
169 throw new Exception( esc_html__(
'Unable to locate product download link.',
'gk-gravityview' ) );
175 $installer->install( $product_download_link );
177 $error = join(
' ', [
178 esc_html__(
'Installation failed.',
'gk-gravityview' ),
185 if ( ! $installer->result ) {
186 throw new Exception( esc_html__(
'Installation failed.',
'gk-gravityview' ) );
189 $product_path = $installer->plugin_info();
191 $activation_error = null;
193 if ( ! is_plugin_active( $product_path ) && ! empty( $product[
'activate'] ) ) {
197 $activation_error = $e->getMessage();
202 'path' => $product_path,
203 'active' => is_plugin_active( $product_path ),
204 'network_activated' => is_plugin_active_for_network( $product_path ),
205 'activation_error' => $activation_error,
221 $payload = wp_parse_args( $payload, [
226 throw new Exception( esc_html__(
'You do not have a permission to perform this action.',
'gk-gravityview' ) );
245 throw new Exception( esc_html__(
'Product is already active.',
'gk-gravityview' ) );
248 $result = activate_plugin( $product,
false, CoreHelpers::is_network_admin() );
251 throw new Exception( esc_html__(
'Could not activate the product.',
'gk-gravityview' ) );
255 'active' => is_plugin_active( $product ),
256 'network_activated' => is_plugin_active_for_network( $product ),
272 $payload = wp_parse_args( $payload, [
277 throw new Exception( esc_html__(
'You do not have a permission to perform this action.',
'gk-gravityview' ) );
296 throw new Exception( esc_html__(
'Product in not active.',
'gk-gravityview' ) );
299 deactivate_plugins( $product,
false, CoreHelpers::is_network_admin() );
302 throw new Exception( esc_html__(
'Could not deactivate the product.',
'gk-gravityview' ) );
306 'active' => is_plugin_active( $product ),
307 'network_activated' => is_plugin_active_for_network( $product ),
323 $payload = wp_parse_args( $payload, [
328 throw new Exception( esc_html__(
'You do not have a permission to perform this action.',
'gk-gravityview' ) );
346 if ( ! file_exists( ABSPATH .
'wp-admin/includes/class-wp-upgrader.php' ) ) {
347 throw new Exception( esc_html__(
'Unable to load core WordPress files required to install the product.',
'gk-gravityview' ) );
350 include_once( ABSPATH .
'wp-admin/includes/class-wp-upgrader.php' );
353 $update_plugins_transient_filter =
function () {
358 $lock_user_agent_header =
function (
$args,
$url ) {
359 if ( strpos(
$url,
'gravitykit.com' ) !== false ) {
360 $args[
'user-agent'] =
'WordPress/' . get_bloginfo(
'version' ) .
'; ' . home_url();
369 add_filter(
'pre_site_transient_update_plugins', $update_plugins_transient_filter );
370 add_filter(
'http_request_args', $lock_user_agent_header, 100, 2 );
372 $updater->upgrade( $product_path );
374 remove_filter(
'pre_site_transient_update_plugins', $update_plugins_transient_filter );
375 remove_filter(
'http_request_args', $lock_user_agent_header, 100 );
377 $error = join(
' ', [
378 esc_html__(
'Update failed.',
'gk-gravityview' ),
379 isset( $updater->strings[ $e->getMessage() ] ) ? $updater->strings[ $e->getMessage() ] : $e->getMessage(),
385 if ( ! $updater->result ) {
386 throw new Exception( esc_html__(
'Installation failed.',
'gk-gravityview' ) );
389 $activation_error = null;
394 $activation_error = $e->getMessage();
400 'active' => $products_data[ $product_path ][
'active'],
401 'network_activated' => $products_data[ $product_path ][
'network_activated'],
402 'installed_version' => $products_data[ $product_path ][
'installed_version'],
403 'update_available' => $products_data[ $product_path ][
'update_available'],
404 'activation_error' => $activation_error,
420 self::EDD_PRODUCTS_API_ENDPOINT,
422 'key' => self::EDD_PRODUCTS_API_KEY,
423 'token' => self::EDD_PRODUCTS_API_TOKEN
430 $remote_products =
Arr::get( $response,
'products', [] );
431 $formatted_products = [];
433 if ( empty( $response ) ) {
434 throw new Exception( esc_html__(
'Invalid product information received from the API.',
'gk-gravityview' ) );
437 foreach ( $remote_products as $remote_product ) {
438 $categories =
Arr::get( $remote_product,
'info.category', [] );
440 if ( empty( $categories ) ) {
444 foreach ( $categories as $category ) {
445 $category_slug =
Arr::get( $category,
'slug' );
446 $category_name =
Arr::get( $category,
'name' );
448 if (
'bundles' === $category_slug ) {
452 if ( !
Arr::get( $formatted_products, $category_slug ) ) {
453 $formatted_products[ $category_slug ] = [
454 'category' => $category_name,
459 $icons = unserialize(
Arr::get( $remote_product,
'readme.icons',
'' ) );
461 $banners = unserialize(
Arr::get( $remote_product,
'readme.banners',
'' ) );
463 $sections = unserialize(
Arr::get( $remote_product,
'readme.sections',
'' ) );
465 $formatted_products[ $category_slug ][
'products'][] = [
466 'id' =>
Arr::get( $remote_product,
'info.id' ),
467 'slug' =>
Arr::get( $remote_product,
'info.slug' ),
468 'text_domain' =>
Arr::get( $remote_product,
'info.textdomain' ),
469 'coming_soon' =>
Arr::get( $remote_product,
'info.coming_soon' ),
470 'title' =>
Arr::get( $remote_product,
'info.title' ),
471 'excerpt' =>
Arr::get( $remote_product,
'info.excerpt' ),
472 'buy_link' =>
Arr::get( $remote_product,
'info.buy_url' ),
473 'link' => esc_url(
Arr::get( $remote_product,
'info.link',
'' ) ),
474 'icon' => esc_url(
Arr::get( $remote_product,
'info.icon',
'' ) ),
476 '1x' => esc_url(
Arr::get( $icons,
'1x',
'' ) ),
477 '2x' => esc_url(
Arr::get( $icons,
'2x',
'' ) ),
480 'low' => esc_url(
Arr::get( $banners,
'low',
'' ) ),
481 'high' => esc_url(
Arr::get( $banners,
'high',
'' ) ),
484 'description' =>
Arr::get( $sections,
'description' ),
485 'changelog' =>
Arr::get( $sections,
'changelog' ),
487 'server_version' =>
Arr::get( $remote_product,
'licensing.version' ),
488 'modified_date' =>
Arr::get( $remote_product,
'info.modified_date' ),
489 'docs' => esc_url(
Arr::get( $remote_product,
'info.docs_url',
'' ) ),
490 'system_requirements' =>
Arr::get( $remote_product,
'system_requirements', [] ),
491 'plugin_dependencies' =>
Arr::get( $remote_product,
'plugin_dependencies', [] ),
496 return $formatted_products;
512 throw new Exception( esc_html__(
'You do not have a permission to perform this action.',
'gk-gravityview' ) );
515 $payload = wp_parse_args( $payload, [
516 'group_by_category' =>
true,
517 'skip_cache' =>
false,
522 foreach ( $products_data as &$category ) {
523 foreach ( $category[
'products'] as &$product ) {
525 foreach ( [
'icons',
'banners',
'sections' ] as $property ) {
526 if ( isset( $product[ $property ] ) ) {
527 unset( $product[ $property ] );
532 $product[
'licenses'] = array_map(
function ( $key ) {
534 }, $product[
'licenses'] );
538 return array_values( $products_data );
554 'skip_cache' =>
false,
555 'group_by_category' =>
false,
561 $products = !
$args[
'skip_cache'] ? get_site_transient( $cache_id ) : null;
571 set_site_transient( $cache_id, json_encode( $products ), DAY_IN_SECONDS );
572 }
else if ( ! is_array( $products ) ) {
573 $products = json_decode( $products,
true );
576 if ( empty( $products ) ) {
577 LoggerFramework::get_instance()->warning(
'Products data is empty.' );
584 $flattened_products = [];
587 foreach ( $products as &$data ) {
588 foreach ( $data[
'products'] as &$product_data ) {
589 $installed_product = CoreHelpers::get_installed_plugin_by_text_domain( $product_data[
'text_domain'] );
598 $product_settings_url = apply_filters(
"gk/foundation/settings/{$product_data['slug']}/settings-url",
'' );
600 $product_data = array_merge( $product_data, [
601 'id' => $product_data[
'id'],
602 'text_domain' => $installed_product ? $installed_product[
'text_domain'] : $product_data[
'text_domain'],
603 'installed' => ! is_null( $installed_product ),
604 'path' => $installed_product ? $installed_product[
'path'] : null,
605 'plugin_file' => $installed_product ? $installed_product[
'plugin_file'] : null,
606 'installed_version' => $installed_product ? $installed_product[
'version'] : null,
607 'update_available' => $installed_product && version_compare( $installed_product[
'version'], $product_data[
'server_version'],
'<' ),
608 'active' => $installed_product ? $installed_product[
'active'] :
false,
609 'network_activated' => $installed_product ? $installed_product[
'network_activated'] :
false,
610 'licenses' => isset( $product_license_map[ $product_data[
'id'] ] ) ? $product_license_map[ $product_data[
'id'] ] : [],
613 'settings' => esc_url( $product_settings_url ),
614 'has_git_folder' => $installed_product && file_exists( dirname( $installed_product[
'plugin_file'] ) .
'/.git' ),
617 if ( !
$args[
'group_by_category'] ) {
618 $key = isset( $product_data[
$args[
'key_by'] ] ) ? $product_data[ $args[
'key_by'] ] : $product_data[
'id'];
620 $flattened_products[ $key ] = $product_data;
625 $products =
$args[
'group_by_category'] ? $products : $flattened_products;
635 $products = apply_filters(
'gk/foundation/products/data', $products,
$args );
648 return CoreHelpers::is_network_admin() ? is_plugin_active_for_network( $plugin ) : is_plugin_active( $plugin );
665 LoggerFramework::get_instance()->warning(
'Unable to get products when adding a badge count for products with updates.' );
672 foreach ( $products_data as $product ) {
673 if ( $product[
'update_available'] && $product[
'active'] && ! empty( $product[
'licenses'] ) ) {
697 $current_system_versions = [
698 'php' => PHP_VERSION,
699 'wp' => get_bloginfo(
'version' ),
702 $failed_requirements = [];
704 if ( empty( $requirements ) ) {
708 foreach ( $requirements as $key => $requirement ) {
709 if ( empty( $requirement[
'version'] ) ) {
713 if ( ! isset( $current_system_versions[ $key ] ) ||
714 version_compare( $current_system_versions[ $key ], $requirement[
'version'],
'<' )
716 $failed_requirements[ $key ] = array_merge( $requirement, [
'version' => $current_system_versions[ $key ] ] );
720 return $failed_requirements ?: null;
733 $failed_dependencies = [];
735 if ( empty( $dependencies ) ) {
739 foreach ( $dependencies as $text_domain => $dependency ) {
740 $installed_dependency = CoreHelpers::get_installed_plugin_by_text_domain( $text_domain );
742 if ( ! $installed_dependency || ! $installed_dependency[
'active'] ) {
743 $failed_dependencies[ $text_domain ] = array_merge( $dependency, [
'version' => null ] );
744 }
else if ( ! empty( $dependency[
'version'] ) && version_compare( $installed_dependency[
'version'], $dependency[
'version'],
'<' ) ) {
745 $failed_dependencies[ $text_domain ] = array_merge( $dependency, [
'version' => $installed_dependency[
'version'] ] );
749 return $failed_dependencies ?: null;
763 static $products_data;
765 if ( ! $products_data ) {
769 LoggerFramework::get_instance()->error(
'Unable to get products when linking to the settings page in the Plugins area.' );
775 $product = isset( $products_data[ $product_path ] ) ? $products_data[ $product_path ] : null;
783 if ( $product[
'settings'] ) {
785 'settings' => sprintf(
786 '<a href="%s">%s</a>',
787 $product[
'settings'],
788 esc_html__(
'Settings',
'gk-gravityview' )
793 $gk_links[
'support'] = sprintf(
794 '<a href="%s">%s</a>',
795 'https://docs.gravitykit.com',
796 esc_html__(
'Support',
'gk-gravityview' )
808 return apply_filters(
"gk/foundation/products/{$product['slug']}/action-links", array_merge( $gk_links, $links ), $gk_links, $links );
install_product(array $product)
Installs a product.
init()
Initializes the class.
ajax_activate_product(array $payload)
AJAX request wrapper for the activate_product() method.
ajax_get_products_data(array $payload)
AJAX request wrapper for the {.
static get_instance()
Returns class instance.
ajax_deactivate_product(array $payload)
AJAX request wrapper for the deactivate_product() method.
static get( $array, $key, $default=null)
{}
const EDD_PRODUCTS_API_KEY
get_products_data(array $args=[])
Returns a list of all GravityKit products with associated installation/activation/licensing data...
ajax_install_product(array $payload)
AJAX request wrapper for the install_product() method.
get_remote_products()
Returns a list of all GravityKit products from the API grouped by category (e.g., plugins...
This is class is used to catch errors and suppress output during product installation/update.
static get_instance( $secret_key='')
Returns class instance.
const EDD_PRODUCTS_API_ENDPOINT
is_product_active_in_current_context( $plugin)
Checks if plugin is activated in the current context (network or site).
deactivate_product( $product)
Deactivates a product.
validate_product_plugin_dependencies( $dependencies)
Validates product plugin dependencies.
static query_api( $url, array $args=[])
Performs remote call to GravityKit's EDD API.
ajax_update_product(array $payload)
AJAX request wrapper for the update_product() method.
static get_instance()
Returns class instance.
static get_instance()
Returns class instance.
const EDD_PRODUCTS_API_TOKEN
validate_product_system_requirements( $requirements)
Validates product system requirements.
update_product( $product_path)
Updates a product.
static get_instance()
Returns class instance.
configure_ajax_routes(array $routes)
Configures AJAX routes handled by this class.
static get_request_unique_string()
Returns a unique value that was generated for this request.
Logging framework for GravityKit.
update_submenu_badge_count()
Optionally updates the Licenses submenu badge count if any of the products have newer versions availa...
display_product_action_links( $links, $product_path)
Displays action links (e.g., Settings, Support, etc.) for each product in the Plugins page...
activate_product( $product)
Activates a product.