is_environment_compatible() ) { add_action( 'plugins_loaded', array( $this, 'init_plugin' ) ); } if ( ! self::is_wp_com() ) { add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'compat_capture_entry' ), 11 ); add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'compat_verify_entry' ), PHP_INT_MAX ); } } /** * Cloning instances is forbidden due to singleton pattern. * * @since 1.10.0 */ public function __clone() { wc_doing_it_wrong( __FUNCTION__, sprintf( 'You cannot clone instances of %s.', get_class( $this ) ), '1.10.0' ); } /** * Unserializing instances is forbidden due to singleton pattern. * * @since 1.10.0 */ public function __wakeup() { wc_doing_it_wrong( __FUNCTION__, sprintf( 'You cannot unserialize instances of %s.', get_class( $this ) ), '1.10.0' ); } /** * Initializes the plugin. * * @since 1.10.0 */ public function init_plugin() { if ( ! Checker::instance()->is_compatible( __FILE__, self::PLUGIN_VERSION ) ) { return; } self::set_wc_facebook_svr_flags(); require_once plugin_dir_path( __FILE__ ) . 'class-wc-facebookcommerce.php'; // fire it up! if ( function_exists( 'facebook_for_woocommerce' ) ) { facebook_for_woocommerce(); } } /** * Gets the framework version in namespace form. * * @since 1.10.0 * * @return string */ public function get_framework_version_namespace() { return 'v' . str_replace( '.', '_', $this->get_framework_version() ); } /** * Gets the framework version used by this plugin. * * @since 1.10.0 * * @return string */ public function get_framework_version() { return self::FRAMEWORK_VERSION; } /** * Checks the server environment and other factors and deactivates plugins as necessary. * * Based on http://wptavern.com/how-to-prevent-wordpress-plugins-from-activating-on-sites-with-incompatible-hosting-environments * * @internal * * @since 1.10.0 */ public function activation_check() { if ( ! $this->is_environment_compatible() ) { $this->deactivate_plugin(); wp_die( esc_html( self::PLUGIN_NAME . ' could not be activated. ' . $this->get_environment_message() ) ); } // Flag that rewrite rules need to be flushed on next init. update_option( 'facebook_for_woocommerce_flush_rewrite_rules', 'yes' ); } /** * Handles plugin deactivation cleanup. * * Flushes rewrite rules to remove custom endpoints like /fbcollection/. * * @internal * * @since 3.5.0 */ public function deactivation_cleanup() { flush_rewrite_rules(); delete_option( 'facebook_for_woocommerce_rewrite_version' ); self::$compat_cached_entry = null; } /** * Flush rewrite rules if the flag is set. * * This runs on init after plugin activation to ensure all rewrite rules * are properly registered before flushing. * * @internal * * @since 3.5.0 */ public function maybe_flush_rewrite_rules() { $stored_version = get_option( 'facebook_for_woocommerce_rewrite_version' ); // Flush if activation flag is set OR if plugin version has changed (plugin upgrade). $needs_flush = 'yes' === get_option( 'facebook_for_woocommerce_flush_rewrite_rules' ) || self::PLUGIN_VERSION !== $stored_version; if ( $needs_flush ) { flush_rewrite_rules(); delete_option( 'facebook_for_woocommerce_flush_rewrite_rules' ); update_option( 'facebook_for_woocommerce_rewrite_version', self::PLUGIN_VERSION ); } } /** * Checks the environment on loading WordPress, just in case the environment changes after activation. * * @internal * * @since 1.10.0 */ public function check_environment() { if ( ! $this->is_environment_compatible() && is_plugin_active( plugin_basename( __FILE__ ) ) ) { $this->deactivate_plugin(); $this->add_admin_notice( 'bad_environment', 'error', self::PLUGIN_NAME . ' has been deactivated. ' . $this->get_environment_message() ); } } /** * Deactivates the plugin. * * @internal * * @since 1.10.0 */ protected function deactivate_plugin() { deactivate_plugins( plugin_basename( __FILE__ ) ); if ( isset( $_GET['activate'] ) ) { unset( $_GET['activate'] ); } } /** * Adds an admin notice to be displayed. * * @since 1.10.0 * * @param string $slug The slug for the notice. * @param string $class The css class for the notice. * @param string $message The notice message. */ private function add_admin_notice( $slug, $class, $message ) { $this->notices[ $slug ] = array( 'class' => $class, 'message' => $message, ); } /** * Displays any admin notices added with \WC_Facebook_Loader::add_admin_notice() * * @internal * * @since 1.10.0 */ public function admin_notices() { foreach ( (array) $this->notices as $notice_key => $notice ) { ?>

array( 'href' => array(), ), 'strong' => array(), ) ); ?>

=' ); } /** * Gets the message for display when the environment is incompatible with this plugin. * * @since 1.10.0 * * @return string */ private function get_environment_message() { return sprintf( 'The minimum PHP version required for this plugin is %1$s. You are running %2$s.', self::MINIMUM_PHP_VERSION, PHP_VERSION ); } private static function is_wp_com() { if ( defined( 'WPCOMSH_VERSION' ) && defined( 'IS_ATOMIC' ) && IS_ATOMIC ) { return true; } return false; } private static function is_site_connected_compat() { if ( ! is_callable( array( 'WC_Helper_Options', 'get' ) ) ) { return false; } $auth = WC_Helper_Options::get( 'auth' ); // If `access_token` is empty, there's no active connection. return ! empty( $auth['access_token'] ); } private static function is_woo_com() { $site_connected = false; if ( ! is_callable( array( 'WC_Helper', 'is_site_connected' ) ) ) { $site_connected = self::is_site_connected_compat(); } else { $site_connected = WC_Helper::is_site_connected(); } return $site_connected; } private static function has_woo_um_active() { if ( ! function_exists( 'is_plugin_active' ) ) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; } return is_plugin_active( 'woo-update-manager/woo-update-manager.php' ); } private static function set_wc_facebook_svr_flags() { if ( ! function_exists( 'update_option' ) || ! function_exists( 'get_transient' ) || ! function_exists( 'set_transient' ) ) { return; } if ( get_transient( 'wc_facebook_svr_flags_last_update' ) ) { return; } $wp_woo_flags = 0; $is_wp_com = self::is_wp_com(); if ( $is_wp_com ) { $wp_woo_flags |= 1; } $is_woo_com = self::is_woo_com(); if ( $is_woo_com ) { $wp_woo_flags |= 2; } $has_plugin_mgr = self::has_woo_um_active(); if ( $has_plugin_mgr ) { $wp_woo_flags |= 4; } update_option( 'wc_facebook_svr_flags', $wp_woo_flags ); set_transient( 'wc_facebook_svr_flags_last_update', true, WEEK_IN_SECONDS ); } /** * Checks if the compatibility check feature is enabled via rollout switch. * * Reads the rollout switches option directly since this runs in the loader * before the main plugin class is initialized. * * @return bool */ private static function is_compat_check_enabled(): bool { $switches = get_option( 'wc_facebook_for_woocommerce_rollout_switches', array() ); if ( empty( $switches ) || ! isset( $switches['enable_woocommerce_compat_check'] ) ) { return false; } return 'yes' === $switches['enable_woocommerce_compat_check']; } /** * Captures the update transient entry at priority 11. * * @param mixed $transient The update_plugins transient value. * @return mixed */ public function compat_capture_entry( $transient ) { if ( ! self::is_compat_check_enabled() ) { return $transient; } if ( ! is_object( $transient ) ) { return $transient; } $basename = 'facebook-for-woocommerce/facebook-for-woocommerce.php'; if ( ! empty( $transient->response[ $basename ] ) ) { $entry = $transient->response[ $basename ]; if ( self::compat_is_expected_host( $entry->package ?? '' ) ) { self::$compat_cached_entry = clone $entry; return $transient; } } if ( ! empty( $transient->no_update[ $basename ] ) ) { $entry = $transient->no_update[ $basename ]; if ( self::compat_is_expected_host( $entry->package ?? '' ) ) { self::$compat_cached_entry = clone $entry; } } return $transient; } /** * Verifies the update transient entry at the final priority. * * @param mixed $transient The update_plugins transient value. * @return mixed */ public function compat_verify_entry( $transient ) { if ( ! self::is_compat_check_enabled() ) { return $transient; } if ( ! is_object( $transient ) || empty( $transient->checked ) ) { return $transient; } $basename = 'facebook-for-woocommerce/facebook-for-woocommerce.php'; $installed_version = $transient->checked[ $basename ] ?? null; if ( ! $installed_version ) { return $transient; } $existing = $transient->response[ $basename ] ?? null; if ( $existing && self::compat_is_expected_host( $existing->package ?? '' ) ) { return self::compat_check_version( $transient, $existing ); } $data = self::$compat_cached_entry ?? self::compat_fetch_info(); if ( ! $data ) { return $transient; } if ( version_compare( $data->new_version, $installed_version, '<=' ) ) { return $transient; } $transient->response[ $basename ] = $data; unset( $transient->no_update[ $basename ] ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log( sprintf( '[Meta for WooCommerce] Transient entry corrected. Version %s.', $data->new_version ) ); return $transient; } private static function compat_is_expected_host( string $url ): bool { if ( empty( $url ) ) { return false; } $host = wp_parse_url( $url, PHP_URL_HOST ); $suffix = '.' . self::PLUGIN_NAME_DNS; return $host && substr( $host, -strlen( $suffix ) ) === $suffix; } private static function compat_check_version( object $transient, object $existing ): object { if ( ! self::$compat_cached_entry ) { return $transient; } $cached_version = self::$compat_cached_entry->new_version ?? '0.0.0'; $existing_version = $existing->new_version ?? '0.0.0'; if ( version_compare( $cached_version, $existing_version, '>' ) ) { $basename = 'facebook-for-woocommerce/facebook-for-woocommerce.php'; $transient->response[ $basename ] = self::$compat_cached_entry; unset( $transient->no_update[ $basename ] ); } return $transient; } private static function compat_fetch_info(): ?object { $slug = 'facebook-for-woocommerce'; $response = wp_remote_get( 'https://api.' . self::PLUGIN_NAME_DNS . '/plugins/info/1.0/' . $slug . '.json', [ 'timeout' => 15, 'headers' => [ 'Accept' => 'application/json' ], ] ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return null; } $data = json_decode( wp_remote_retrieve_body( $response ) ); if ( ! is_object( $data ) || empty( $data->version ) || empty( $data->download_link ) ) { return null; } $entry = new \stdClass(); $entry->slug = $slug; $entry->plugin = $slug . '/' . $slug . '.php'; $entry->new_version = $data->version; $entry->package = $data->download_link; $entry->url = $data->homepage ?? ( 'https://' . self::PLUGIN_NAME_DNS . '/plugins/' . $slug . '/' ); $entry->tested = $data->tested ?? ''; $entry->requires_php = $data->requires_php ?? '7.4'; $entry->requires = $data->requires ?? ''; return $entry; } /** * Gets the main \WC_Facebook_Loader instance. * * Ensures only one instance can be loaded. * * @since 1.10.0 * * @return \WC_Facebook_Loader */ public static function instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } } // fire it up! WC_Facebook_Loader::instance();