8889841cPackage.php000064400000006174150514772740006634 0ustar00version = $version; $this->path = $plugin_path; $this->feature_gating = $feature_gating; } /** * Returns the version of the plugin. * * @return string */ public function get_version() { return $this->version; } /** * Returns the version of the plugin stored in the database. * * @return string */ public function get_version_stored_on_db() { return get_option( Options::WC_BLOCK_VERSION, '' ); } /** * Set the version of the plugin stored in the database. * This is useful during the first installation or after the upgrade process. */ public function set_version_stored_on_db() { update_option( Options::WC_BLOCK_VERSION, $this->get_version() ); } /** * Returns the path to the plugin directory. * * @param string $relative_path If provided, the relative path will be * appended to the plugin path. * * @return string */ public function get_path( $relative_path = '' ) { return trailingslashit( $this->path ) . $relative_path; } /** * Returns the url to the blocks plugin directory. * * @param string $relative_url If provided, the relative url will be * appended to the plugin url. * * @return string */ public function get_url( $relative_url = '' ) { if ( ! $this->plugin_dir_url ) { // Append index.php so WP does not return the parent directory. $this->plugin_dir_url = plugin_dir_url( $this->path . '/index.php' ); } return $this->plugin_dir_url . $relative_url; } /** * Returns an instance of the the FeatureGating class. * * @return FeatureGating */ public function feature() { return $this->feature_gating; } /** * Checks if we're executing the code in an experimental build mode. * * @return boolean */ public function is_experimental_build() { return $this->feature()->is_experimental_build(); } /** * Checks if we're executing the code in an feature plugin or experimental build mode. * * @return boolean */ public function is_feature_plugin_build() { return $this->feature()->is_feature_plugin_build(); } } Bootstrap.php000064400000037121150514772740007252 0ustar00container = $container; $this->package = $container->get( Package::class ); $this->migration = $container->get( Migration::class ); if ( $this->has_core_dependencies() ) { $this->init(); /** * Fires when the woocommerce blocks are loaded and ready to use. * * This hook is intended to be used as a safe event hook for when the plugin * has been loaded, and all dependency requirements have been met. * * To ensure blocks are initialized, you must use the `woocommerce_blocks_loaded` * hook instead of the `plugins_loaded` hook. This is because the functions * hooked into plugins_loaded on the same priority load in an inconsistent and unpredictable manner. * * @since 2.5.0 */ do_action( 'woocommerce_blocks_loaded' ); } } /** * Init the package - load the blocks library and define constants. */ protected function init() { $this->register_dependencies(); $this->register_payment_methods(); if ( $this->package->is_experimental_build() && is_admin() ) { if ( $this->package->get_version() !== $this->package->get_version_stored_on_db() ) { $this->migration->run_migrations(); $this->package->set_version_stored_on_db(); } } add_action( 'admin_init', function() { // Delete this notification because the blocks are included in WC Core now. This will handle any sites // with lingering notices. InboxNotifications::delete_surface_cart_checkout_blocks_notification(); }, 10, 0 ); $is_rest = wc()->is_rest_api_request(); // Load assets in admin and on the frontend. if ( ! $is_rest ) { $this->add_build_notice(); $this->container->get( AssetDataRegistry::class ); $this->container->get( Installer::class ); $this->container->get( AssetsController::class ); } $this->container->get( DraftOrders::class )->init(); $this->container->get( CreateAccount::class )->init(); $this->container->get( Notices::class )->init(); $this->container->get( StoreApi::class )->init(); $this->container->get( GoogleAnalytics::class ); $this->container->get( BlockTypesController::class ); $this->container->get( BlockTemplatesController::class ); $this->container->get( ProductSearchResultsTemplate::class ); $this->container->get( ProductAttributeTemplate::class ); $this->container->get( ClassicTemplatesCompatibility::class ); $this->container->get( ArchiveProductTemplatesCompatibility::class )->init(); $this->container->get( SingleProductTemplateCompatibility::class )->init(); $this->container->get( BlockPatterns::class ); $this->container->get( PaymentsApi::class ); $this->container->get( ShippingController::class )->init(); } /** * Check core dependencies exist. * * @return boolean */ protected function has_core_dependencies() { $has_needed_dependencies = class_exists( 'WooCommerce', false ); if ( $has_needed_dependencies ) { $plugin_data = \get_file_data( $this->package->get_path( 'woocommerce-gutenberg-products-block.php' ), [ 'RequiredWCVersion' => 'WC requires at least', ] ); if ( isset( $plugin_data['RequiredWCVersion'] ) && version_compare( \WC()->version, $plugin_data['RequiredWCVersion'], '<' ) ) { $has_needed_dependencies = false; add_action( 'admin_notices', function() { if ( should_display_compatibility_notices() ) { ?>

package->get_path( 'build/featured-product.js' ) ); } /** * Add a notice stating that the build has not been done yet. */ protected function add_build_notice() { if ( $this->is_built() ) { return; } add_action( 'admin_notices', function() { echo '

'; printf( /* translators: %1$s is the install command, %2$s is the build command, %3$s is the watch command. */ esc_html__( 'WooCommerce Blocks development mode requires files to be built. From the plugin directory, run %1$s to install dependencies, %2$s to build the files or %3$s to build the files and watch for changes.', 'woocommerce' ), 'npm install', 'npm run build', 'npm start' ); echo '

'; } ); } /** * Register core dependencies with the container. */ protected function register_dependencies() { $this->container->register( FeatureGating::class, function () { return new FeatureGating(); } ); $this->container->register( AssetApi::class, function ( Container $container ) { return new AssetApi( $container->get( Package::class ) ); } ); $this->container->register( AssetDataRegistry::class, function( Container $container ) { return new AssetDataRegistry( $container->get( AssetApi::class ) ); } ); $this->container->register( AssetsController::class, function( Container $container ) { return new AssetsController( $container->get( AssetApi::class ) ); } ); $this->container->register( PaymentMethodRegistry::class, function() { return new PaymentMethodRegistry(); } ); $this->container->register( Installer::class, function () { return new Installer(); } ); $this->container->register( BlockTypesController::class, function ( Container $container ) { $asset_api = $container->get( AssetApi::class ); $asset_data_registry = $container->get( AssetDataRegistry::class ); return new BlockTypesController( $asset_api, $asset_data_registry ); } ); $this->container->register( BlockTemplatesController::class, function ( Container $container ) { return new BlockTemplatesController( $container->get( Package::class ) ); } ); $this->container->register( ProductSearchResultsTemplate::class, function () { return new ProductSearchResultsTemplate(); } ); $this->container->register( ProductAttributeTemplate::class, function () { return new ProductAttributeTemplate(); } ); $this->container->register( ClassicTemplatesCompatibility::class, function ( Container $container ) { $asset_data_registry = $container->get( AssetDataRegistry::class ); return new ClassicTemplatesCompatibility( $asset_data_registry ); } ); $this->container->register( ArchiveProductTemplatesCompatibility::class, function () { return new ArchiveProductTemplatesCompatibility(); } ); $this->container->register( SingleProductTemplateCompatibility::class, function () { return new SingleProductTemplateCompatibility(); } ); $this->container->register( DraftOrders::class, function( Container $container ) { return new DraftOrders( $container->get( Package::class ) ); } ); $this->container->register( CreateAccount::class, function( Container $container ) { return new CreateAccount( $container->get( Package::class ) ); } ); $this->container->register( GoogleAnalytics::class, function( Container $container ) { // Require Google Analytics Integration to be activated. if ( ! class_exists( 'WC_Google_Analytics_Integration', false ) ) { return; } $asset_api = $container->get( AssetApi::class ); return new GoogleAnalytics( $asset_api ); } ); $this->container->register( Notices::class, function( Container $container ) { return new Notices( $container->get( Package::class ) ); } ); $this->container->register( PaymentsApi::class, function ( Container $container ) { $payment_method_registry = $container->get( PaymentMethodRegistry::class ); $asset_data_registry = $container->get( AssetDataRegistry::class ); return new PaymentsApi( $payment_method_registry, $asset_data_registry ); } ); $this->container->register( StoreApi::class, function () { return new StoreApi(); } ); // Maintains backwards compatibility with previous Store API namespace. $this->container->register( 'Automattic\WooCommerce\Blocks\StoreApi\Formatters', function( Container $container ) { $this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\Formatters', '7.2.0', 'Automattic\WooCommerce\StoreApi\Formatters', '7.4.0' ); return $container->get( StoreApi::class )->container()->get( \Automattic\WooCommerce\StoreApi\Formatters::class ); } ); $this->container->register( 'Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi', function( Container $container ) { $this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi', '7.2.0', 'Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema', '7.4.0' ); return $container->get( StoreApi::class )->container()->get( \Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema::class ); } ); $this->container->register( 'Automattic\WooCommerce\Blocks\StoreApi\SchemaController', function( Container $container ) { $this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\SchemaController', '7.2.0', 'Automattic\WooCommerce\StoreApi\SchemaController', '7.4.0' ); return $container->get( StoreApi::class )->container()->get( SchemaController::class ); } ); $this->container->register( 'Automattic\WooCommerce\Blocks\StoreApi\RoutesController', function( Container $container ) { $this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\RoutesController', '7.2.0', 'Automattic\WooCommerce\StoreApi\RoutesController', '7.4.0' ); return $container->get( StoreApi::class )->container()->get( RoutesController::class ); } ); $this->container->register( BlockPatterns::class, function () { return new BlockPatterns( $this->package ); } ); $this->container->register( ShippingController::class, function ( $container ) { $asset_api = $container->get( AssetApi::class ); $asset_data_registry = $container->get( AssetDataRegistry::class ); return new ShippingController( $asset_api, $asset_data_registry ); } ); } /** * Throws a deprecation notice for a dependency without breaking requests. * * @param string $function Class or function being deprecated. * @param string $version Version in which it was deprecated. * @param string $replacement Replacement class or function, if applicable. * @param string $trigger_error_version Optional version to start surfacing this as a PHP error rather than a log. Defaults to $version. */ protected function deprecated_dependency( $function, $version, $replacement = '', $trigger_error_version = '' ) { if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { return; } $trigger_error_version = $trigger_error_version ? $trigger_error_version : $version; $error_message = $replacement ? sprintf( '%1$s is deprecated since version %2$s! Use %3$s instead.', $function, $version, $replacement ) : sprintf( '%1$s is deprecated since version %2$s with no alternative available.', $function, $version ); /** * Fires when a deprecated function is called. * * @since 7.3.0 */ do_action( 'deprecated_function_run', $function, $replacement, $version ); $log_error = false; // If headers have not been sent yet, log to avoid breaking the request. if ( ! headers_sent() ) { $log_error = true; } // If the $trigger_error_version was not yet reached, only log the error. if ( version_compare( $this->package->get_version(), $trigger_error_version, '<' ) ) { $log_error = true; } /** * Filters whether to trigger an error for deprecated functions. (Same as WP core) * * @since 7.3.0 * * @param bool $trigger Whether to trigger the error for deprecated functions. Default true. */ if ( ! apply_filters( 'deprecated_function_trigger_error', true ) ) { $log_error = true; } if ( $log_error ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log( $error_message ); } else { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.PHP.DevelopmentFunctions.error_log_trigger_error trigger_error( $error_message, E_USER_DEPRECATED ); } } /** * Register payment method integrations with the container. */ protected function register_payment_methods() { $this->container->register( Cheque::class, function( Container $container ) { $asset_api = $container->get( AssetApi::class ); return new Cheque( $asset_api ); } ); $this->container->register( PayPal::class, function( Container $container ) { $asset_api = $container->get( AssetApi::class ); return new PayPal( $asset_api ); } ); $this->container->register( BankTransfer::class, function( Container $container ) { $asset_api = $container->get( AssetApi::class ); return new BankTransfer( $asset_api ); } ); $this->container->register( CashOnDelivery::class, function( Container $container ) { $asset_api = $container->get( AssetApi::class ); return new CashOnDelivery( $asset_api ); } ); } } Services/GoogleAnalytics.php000064400000006255150514772740012150 0ustar00asset_api = $asset_api; $this->init(); } /** * Hook into WP. */ protected function init() { add_action( 'init', array( $this, 'register_assets' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_filter( 'script_loader_tag', array( $this, 'async_script_loader_tags' ), 10, 3 ); } /** * Register scripts. */ public function register_assets() { $this->asset_api->register_script( 'wc-blocks-google-analytics', 'build/wc-blocks-google-analytics.js', [ 'google-tag-manager' ] ); } /** * Enqueue the Google Tag Manager script if prerequisites are met. */ public function enqueue_scripts() { $settings = $this->get_google_analytics_settings(); $prefix = strstr( strtoupper( $settings['ga_id'] ), '-', true ); // Require tracking to be enabled with a valid GA ID. if ( ! in_array( $prefix, [ 'G', 'GT' ], true ) ) { return; } /** * Filter to disable Google Analytics tracking. * * @internal Matches filter name in GA extension. * @since 4.9.0 * * @param boolean $disable_tracking If true, tracking will be disabled. */ if ( apply_filters( 'woocommerce_ga_disable_tracking', ! wc_string_to_bool( $settings['ga_event_tracking_enabled'] ) ) ) { return; } if ( ! wp_script_is( 'google-tag-manager', 'registered' ) ) { // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion wp_register_script( 'google-tag-manager', 'https://www.googletagmanager.com/gtag/js?id=' . $settings['ga_id'], [], null, false ); wp_add_inline_script( 'google-tag-manager', " window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', '" . esc_js( $settings['ga_id'] ) . "', { 'send_page_view': false });" ); } wp_enqueue_script( 'wc-blocks-google-analytics' ); } /** * Get settings from the GA integration extension. * * @return array */ private function get_google_analytics_settings() { return wp_parse_args( get_option( 'woocommerce_google_analytics_settings' ), [ 'ga_id' => '', 'ga_event_tracking_enabled' => 'no', ] ); } /** * Add async to script tags with defined handles. * * @param string $tag HTML for the script tag. * @param string $handle Handle of script. * @param string $src Src of script. * @return string */ public function async_script_loader_tags( $tag, $handle, $src ) { if ( ! in_array( $handle, array( 'google-tag-manager' ), true ) ) { return $tag; } // If script was output manually in wp_head, abort. if ( did_action( 'woocommerce_gtag_snippet' ) ) { return ''; } return str_replace( '