<?php /** * Plugin Installer class - responsible for installing other plugins. * * @package ocdi */ namespace OCDI; class PluginInstaller { /** * Holds all registered plugins. * * @var array */ private $plugins; /** * Initialize everything needed for the plugin installer class to function properly. */ public function init() { $this->set_plugins(); add_action( 'ocdi/plugin_intaller_before_plugin_activation', array( $this, 'before_plugin_activation' ) ); add_action( 'ocdi/plugin_intaller_after_plugin_activation', array( $this, 'after_plugin_activation' ) ); add_action( 'wp_ajax_ocdi_install_plugin', array( $this, 'install_plugin_callback' ) ); } /** * Prevent the auto redirects for our recommended plugins. * This code is run before plugin is activated. * * @param string $slug The plugin slug. */ public function before_plugin_activation( $slug ) { // Disable the WPForms redirect after plugin activation. if ( $slug === 'wpforms-lite' ) { update_option( 'wpforms_activation_redirect', true ); } // Disable the AIOSEO redirect after plugin activation. if ( $slug === 'all-in-one-seo-pack' ) { update_option( 'aioseo_activation_redirect', true ); } // Disable the WP Mail SMTP redirect after plugin activation. if ( $slug === 'wp-mail-smtp' ) { update_option( 'wp_mail_smtp_activation_prevent_redirect', true ); } } /** * Prevent the auto redirects for our recommended plugins. * This code is run after plugin is activated. * * @param string $slug The plugin slug. */ public function after_plugin_activation( $slug ) { // Disable the RafflePress redirect after plugin activation. if ( $slug === 'rafflepress' ) { delete_transient('_rafflepress_welcome_screen_activation_redirect'); } // Disable the MonsterInsights redirect after plugin activation. if ( $slug === 'google-analytics-for-wordpress' ) { delete_transient('_monsterinsights_activation_redirect'); } // Disable the SeedProd redirect after the plugin activation. if ( $slug === 'coming-soon' ) { delete_transient( '_seedprod_welcome_screen_activation_redirect' ); } } /** * Get all partner plugins data. * * @return array[] */ public function get_partner_plugins() { return array( array( 'name' => esc_html__( 'WPForms', 'one-click-demo-import' ), 'description' => esc_html__( 'Join 6,000,000+ professionals who build smarter forms and surveys with WPForms.', 'one-click-demo-import' ), 'slug' => 'wpforms-lite', 'required' => false, 'preselected' => true, ), array( 'name' => esc_html__( 'All in One SEO', 'one-click-demo-import' ), 'description' => esc_html__( 'Use All in One SEO Pack to optimize your WordPress site for SEO.', 'one-click-demo-import' ), 'slug' => 'all-in-one-seo-pack', 'required' => false, 'preselected' => true, ), array( 'name' => esc_html__( 'MonsterInsights', 'one-click-demo-import' ), 'description' => esc_html__( 'The #1 Google Analytics Plugin for WordPress that’s easy and powerful.', 'one-click-demo-import' ), 'slug' => 'google-analytics-for-wordpress', 'required' => false, 'preselected' => true, ), array( 'name' => esc_html__( 'Custom Landing Pages by SeedProd', 'one-click-demo-import' ), 'description' => esc_html__( 'Work on your site in private while visitors see a "Coming Soon" or "Maintenance Mode" page.', 'one-click-demo-import' ), 'slug' => 'coming-soon', 'required' => false, ), array( 'name' => esc_html__( 'Smash Balloon Social Photo Feed', 'one-click-demo-import' ), 'description' => esc_html__( 'Display beautifully clean, customizable, and responsive Instagram feeds.', 'one-click-demo-import' ), 'slug' => 'instagram-feed', 'required' => false, ), array( 'name' => esc_html__( 'WP Mail SMTP', 'one-click-demo-import' ), 'description' => esc_html__( 'Make email delivery easy for WordPress. Connect with SMTP, Gmail, Outlook, Mailgun, and more.', 'one-click-demo-import' ), 'slug' => 'wp-mail-smtp', 'required' => false, ), ); } /** * Set all registered plugins. * With our recommended plugins being set as defaults. */ public function set_plugins() { $all_plugins = array_merge( $this->get_partner_plugins(), Helpers::apply_filters( 'ocdi/register_plugins', array() ) ); $this->plugins = $this->filter_plugins( $all_plugins ); } /** * Get all theme registered plugins. * With our 3 top recommended plugins being set as defaults. */ public function get_theme_plugins() { $default_plugins = $this->get_top_partner_plugins(); $theme_plugins = array_merge( $default_plugins, Helpers::apply_filters( 'ocdi/register_plugins', array() ) ); return $this->filter_plugins( $theme_plugins ); } /** * Get the top 3 partner plugins if they are not already activated. * * @return array */ private function get_top_partner_plugins() { $installed_plugins = $this->get_plugins(); $contact_form = [ 'wpforms-lite/wpforms.php', 'wpforms/wpforms.php', 'formidable/formidable.php', 'formidable/formidable-pro.php', 'gravityforms/gravityforms.php', 'ninja-forms/ninja-forms.php', ]; $seo = [ 'all-in-one-seo-pack/all_in_one_seo_pack.php', 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php', 'wordpress-seo/wp-seo.php', 'wordpress-seo-premium/wp-seo-premium.php', 'seo-by-rank-math/rank-math.php', 'seo-by-rank-math-pro/rank-math-pro.php', ]; $google_analytics = [ 'google-analytics-for-wordpress/googleanalytics.php', 'exactmetrics-premium/exactmetrics-premium.php', 'google-analytics-dashboard-for-wp/gadwp.php', ]; $plugins = array_slice( $this->get_partner_plugins(), 0, 3 ); $plugins = array_map( function ( $plugin ) { unset( $plugin['preselected'] ); return $plugin; } , $plugins ); return array_filter( $plugins, function ( $plugin ) use ( $installed_plugins, $contact_form, $seo, $google_analytics ) { if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) { return false; } if ( 'wpforms-lite' === $plugin['slug'] ) { foreach ( $installed_plugins as $basename => $plugin_info ) { if ( in_array( $basename, $contact_form, true ) ) { return false; } } } elseif ( 'all-in-one-seo-pack' === $plugin['slug'] ) { foreach ( $installed_plugins as $basename => $plugin_info ) { if ( in_array( $basename, $seo, true ) ) { return false; } } } elseif ( 'google-analytics-for-wordpress' === $plugin['slug'] ) { foreach ( $installed_plugins as $basename => $plugin_info ) { if ( in_array( $basename, $google_analytics, true ) ) { return false; } } } return true; } ); } /** * AJAX callback for installing a plugin. * Has to contain the `slug` POST parameter. */ public function install_plugin_callback() { check_ajax_referer( 'ocdi-ajax-verification', 'security' ); // Check if user has the WP capability to install plugins. if ( ! current_user_can( 'install_plugins' ) ) { wp_send_json_error( esc_html__( 'Could not install the plugin. You don\'t have permission to install plugins.', 'one-click-demo-import' ) ); } $slug = ! empty( $_POST['slug'] ) ? sanitize_key( wp_unslash( $_POST['slug'] ) ) : ''; if ( empty( $slug ) ) { wp_send_json_error( esc_html__( 'Could not install the plugin. Plugin slug is missing.', 'one-click-demo-import' ) ); } // Check if the plugin is already installed and activated. if ( $this->is_plugin_active( $slug ) ) { wp_send_json_success( esc_html__( 'Plugin is already installed and activated!', 'one-click-demo-import' ) ); } // Activate the plugin if the plugin is already installed. if ( $this->is_plugin_installed( $slug ) ) { $activated = $this->activate_plugin( $this->get_plugin_basename_from_slug( $slug ), $slug ); if ( ! is_wp_error( $activated ) ) { wp_send_json_success( esc_html__( 'Plugin was already installed! We activated it for you.', 'one-click-demo-import' ) ); } else { wp_send_json_error( $activated->get_error_message() ); } } // Check for file system permissions. if ( ! $this->filesystem_permissions_allowed() ) { wp_send_json_error( esc_html__( 'Could not install the plugin. Don\'t have file permission.', 'one-click-demo-import' ) ); } // Do not allow WordPress to search/download translations, as this will break JS output. remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 ); // Prep variables for Plugin_Installer_Skin class. $extra = array(); $extra['slug'] = $slug; // Needed for potentially renaming of directory name. $source = $this->get_download_url( $slug ); $api = empty( $this->get_plugin_data( $slug )['source'] ) ? $this->get_plugins_api( $slug ) : null; $api = ( false !== $api ) ? $api : null; if ( ! empty( $api ) && is_wp_error( $api ) ) { wp_send_json_error( $api->get_error_message() ); } if ( ! class_exists( '\Plugin_Upgrader', false ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; } $skin_args = array( 'type' => 'web', 'plugin' => '', 'api' => $api, 'extra' => $extra, ); $upgrader = new \Plugin_Upgrader( new PluginInstallerSkin( $skin_args ) ); $upgrader->install( $source ); // Flush the cache and return the newly installed plugin basename. wp_cache_flush(); if ( $upgrader->plugin_info() ) { $activated = $this->activate_plugin( $upgrader->plugin_info(), $slug ); if ( ! is_wp_error( $activated ) ) { wp_send_json_success( esc_html__( 'Plugin installed and activated succesfully.', 'one-click-demo-import' ) ); } else { wp_send_json_success( $activated->get_error_message() ); } } wp_send_json_error( esc_html__( 'Could not install the plugin. WP Plugin installer could not retrieve plugin information.', 'one-click-demo-import' ) ); } /** * Direct plugin install, without AJAX responses. * * @param string $slug The registered plugin slug to install. * * @return bool */ public function install_plugin( $slug ) { if ( empty( $slug ) ) { return false; } // Check if user has the WP capability to install plugins. if ( ! current_user_can( 'install_plugins' ) ) { return false; } // Check if the plugin is already installed and activated. if ( $this->is_plugin_active( $slug ) ) { return true; } // Activate the plugin if the plugin is already installed. if ( $this->is_plugin_installed( $slug ) ) { $activated = $this->activate_plugin( $this->get_plugin_basename_from_slug( $slug ), $slug ); return ! is_wp_error( $activated ); } // Check for file system permissions. if ( ! $this->filesystem_permissions_allowed() ) { return false; } // Do not allow WordPress to search/download translations, as this will break JS output. remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 ); // Prep variables for Plugin_Installer_Skin class. $extra = array(); $extra['slug'] = $slug; // Needed for potentially renaming of directory name. $source = $this->get_download_url( $slug ); $api = empty( $this->get_plugin_data( $slug )['source'] ) ? $this->get_plugins_api( $slug ) : null; $api = ( false !== $api ) ? $api : null; if ( ! empty( $api ) && is_wp_error( $api ) ) { return false; } if ( ! class_exists( '\Plugin_Upgrader', false ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; } $skin_args = array( 'type' => 'web', 'plugin' => '', 'api' => $api, 'extra' => $extra, ); $upgrader = new \Plugin_Upgrader( new PluginInstallerSkinSilent( $skin_args ) ); $upgrader->install( $source ); // Flush the cache and return the newly installed plugin basename. wp_cache_flush(); if ( $upgrader->plugin_info() ) { $activated = $this->activate_plugin( $upgrader->plugin_info(), $slug ); if ( ! is_wp_error( $activated ) ) { return true; } } return false; } /** * Activate the plugin with the before and after hooks. * * @param string $plugin_filename The plugin's basename(example: wpforms/wpforms.php). * @param string $slug The plugin's slug. * * @return null|WP_Error Null on success, WP_Error on invalid file. */ private function activate_plugin( $plugin_filename, $slug ) { Helpers::do_action( 'ocdi/plugin_intaller_before_plugin_activation', $slug ); $activated = activate_plugin( $plugin_filename ); Helpers::do_action( 'ocdi/plugin_intaller_after_plugin_activation', $slug ); return $activated; } /** * Helper function to check for the filesystem permissions. * * @return bool */ private function filesystem_permissions_allowed() { $ocdi = OneClickDemoImport::get_instance(); $url = esc_url_raw( $ocdi->get_plugin_settings_url() ); $creds = request_filesystem_credentials( $url, '', false, false, null ); // Check for file system permissions. if ( false === $creds || ! WP_Filesystem( $creds ) ) { return false; } return true; } /** * Get the data of a registered plugin via the slug. * * @param string $slug The plugin slug. * * @return array */ public function get_plugin_data( $slug ) { $data = []; foreach ( $this->plugins as $plugin ) { if ( $plugin['slug'] === $slug ) { $data = $plugin; break; } } return $data; } /** * Get the download URL for a plugin. * * @param string $slug Plugin slug. * * @return string Plugin download URL. */ public function get_download_url( $slug ) { $plugin_data = $this->get_plugin_data( $slug ); if ( ! empty( $plugin_data['source'] ) ) { return $plugin_data['source']; } return $this->get_wp_repo_download_url( $slug ); } /** * Get the download URL from the WP.org. * * @param string $slug Plugin slug. * * @return string Plugin download URL from WP.org. */ protected function get_wp_repo_download_url( $slug ) { $source = ''; $api = $this->get_plugins_api( $slug ); if ( false !== $api && isset( $api->download_link ) ) { $source = $api->download_link; } return $source; } /** * Try to grab information from WordPress API. * * @param string $slug Plugin slug. * * @return object Plugins_api response object on success, WP_Error on failure. */ protected function get_plugins_api( $slug ) { static $api = array(); // Cache received responses. if ( ! isset( $api[ $slug ] ) ) { if ( ! function_exists( 'plugins_api' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; } $api[ $slug ] = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) ); } return $api[ $slug ]; } /** * Wrapper around the core WP get_plugins function, making sure it's actually available. * * @param string $plugin_folder Optional. Relative path to single plugin folder. * * @return array Array of installed plugins with plugin information. */ public function get_plugins( $plugin_folder = '' ) { if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } return get_plugins( $plugin_folder ); } /** * Helper function to extract the plugin file path from the * plugin slug, if the plugin is installed. * * @param string $slug Plugin slug (typically folder name) as provided by the developer. * * @return string|bool Either plugin file path for plugin if installed, or false. */ protected function get_plugin_basename_from_slug( $slug ) { $keys = array_keys( $this->get_plugins() ); foreach ( $keys as $key ) { if ( preg_match( '/^' . $slug . '\//', $key ) ) { return $key; } } return false; } /** * Check if a plugin is installed. Does not take must-use plugins into account. * * @param string $slug Plugin slug. * * @return bool True if installed, false otherwise. */ public function is_plugin_installed( $slug ) { return ( ! empty( $this->get_plugin_basename_from_slug( $slug ) ) ); } /** * Check if a plugin is active. * * @param string $slug Plugin slug. * * @return bool True if active, false otherwise. */ public function is_plugin_active( $slug ) { $plugin_path = $this->get_plugin_basename_from_slug( $slug ); if ( empty( $plugin_path ) ) { return false; } return is_plugin_active( $plugin_path ); } /** * Get the list of plugins (with their data) of all non-active and non-installed registered plugins. * * @return array */ public function get_missing_plugins() { $missing = []; foreach ( $this->plugins as $plugin_data ) { if ( ! $this->is_plugin_active( $plugin_data['slug'] ) ) { $missing[] = $plugin_data; } } return $missing; } /** * Return only plugins with required attributes: * - name * - slug * * @param array $plugins The array of plugin's data. * * @return array */ private function filter_plugins( $plugins ) { return array_filter( $plugins, function ( $plugin ) { if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) { return false; } return true; } ); } }