/** * Copyright (C) 2014-2025 ServMask Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Attribution: This code is part of the All-in-One WP Migration plugin, developed by * * ███████╗███████╗██████╗ ██╗ ██╗███╗ ███╗ █████╗ ███████╗██╗ ██╗ * ██╔════╝██╔════╝██╔══██╗██║ ██║████╗ ████║██╔══██╗██╔════╝██║ ██╔╝ * ███████╗█████╗ ██████╔╝██║ ██║██╔████╔██║███████║███████╗█████╔╝ * ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██║╚██╔╝██║██╔══██║╚════██║██╔═██╗ * ███████║███████╗██║ ██║ ╚████╔╝ ██║ ╚═╝ ██║██║ ██║███████║██║ ██╗ * ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ */ if ( ! defined( 'ABSPATH' ) ) { die( 'Kangaroos cannot jump here' ); } /** * @method RecursiveIterator getInnerIterator() */ class Ai1wm_Recursive_Extension_Filter extends RecursiveFilterIterator { protected $include = array(); public function __construct( RecursiveIterator $iterator, $include = array() ) { parent::__construct( $iterator ); if ( is_array( $include ) ) { $this->include = $include; } } #[\ReturnTypeWillChange] public function accept() { if ( $this->getInnerIterator()->isFile() ) { if ( ! in_array( pathinfo( $this->getInnerIterator()->getFilename(), PATHINFO_EXTENSION ), $this->include ) ) { return false; } } return true; } #[\ReturnTypeWillChange] public function getChildren() { return new self( $this->getInnerIterator()->getChildren(), $this->include ); } } /** * Copyright (C) 2014-2025 ServMask Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Attribution: This code is part of the All-in-One WP Migration plugin, developed by * * ███████╗███████╗██████╗ ██╗ ██╗███╗ ███╗ █████╗ ███████╗██╗ ██╗ * ██╔════╝██╔════╝██╔══██╗██║ ██║████╗ ████║██╔══██╗██╔════╝██║ ██╔╝ * ███████╗█████╗ ██████╔╝██║ ██║██╔████╔██║███████║███████╗█████╔╝ * ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██║╚██╔╝██║██╔══██║╚════██║██╔═██╗ * ███████║███████╗██║ ██║ ╚████╔╝ ██║ ╚═╝ ██║██║ ██║███████║██║ ██╗ * ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ */ if ( ! defined( 'ABSPATH' ) ) { die( 'Kangaroos cannot jump here' ); } /** * @method string getSubPathname() * @method string getPathname() * @method string getSubPath() * @method int getSize() * @method int getMTime() */ class Ai1wm_Recursive_Iterator_Iterator extends RecursiveIteratorIterator { } /** * Copyright (C) 2014-2025 ServMask Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Attribution: This code is part of the All-in-One WP Migration plugin, developed by * * ███████╗███████╗██████╗ ██╗ ██╗███╗ ███╗ █████╗ ███████╗██╗ ██╗ * ██╔════╝██╔════╝██╔══██╗██║ ██║████╗ ████║██╔══██╗██╔════╝██║ ██╔╝ * ███████╗█████╗ ██████╔╝██║ ██║██╔████╔██║███████║███████╗█████╔╝ * ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██║╚██╔╝██║██╔══██║╚════██║██╔═██╗ * ███████║███████╗██║ ██║ ╚████╔╝ ██║ ╚═╝ ██║██║ ██║███████║██║ ██╗ * ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ */ if ( ! defined( 'ABSPATH' ) ) { die( 'Kangaroos cannot jump here' ); } class Ai1wm_Import_Controller { public static function index() { Ai1wm_Template::render( 'import/index' ); } public static function import( $params = array() ) { global $ai1wm_params; // Set params if ( empty( $params ) ) { $params = stripslashes_deep( array_merge( $_GET, $_POST ) ); } // Set priority if ( ! isset( $params['priority'] ) ) { $params['priority'] = 10; } $ai1wm_params = $params; // Set job ID for per-job status tracking if ( isset( $params['storage'] ) ) { Ai1wm_Status::$job_id = $params['storage']; } // Set secret key $secret_key = null; if ( isset( $params['secret_key'] ) ) { $secret_key = trim( $params['secret_key'] ); } ai1wm_setup_environment(); try { // Ensure that unauthorized people cannot access import action ai1wm_verify_secret_key( $secret_key ); } catch ( Ai1wm_Not_Valid_Secret_Key_Exception $e ) { exit; } // Error handling is set up for the authorised request, after the secret-key check. ai1wm_setup_errors(); // Loop over filters if ( ( $filters = ai1wm_get_filters( 'ai1wm_import' ) ) ) { while ( $hooks = current( $filters ) ) { if ( intval( $params['priority'] ) === key( $filters ) ) { foreach ( $hooks as $hook ) { try { // Run function hook $params = call_user_func_array( $hook['function'], array( $params ) ); } catch ( Ai1wm_Upload_Exception $e ) { do_action( 'ai1wm_status_upload_error', $params, $e ); if ( defined( 'WP_CLI' ) ) { /* translators: 1: Error code, 2: Error message. */ WP_CLI::error( sprintf( __( 'Import failed. Code: %1$s. %2$s', 'all-in-one-wp-migration' ), $e->getCode(), $e->getMessage() ) ); } // REST API: write job-scoped error so poll clients see the terminal state, then re-throw if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { Ai1wm_Status::error( __( 'Import failed', 'all-in-one-wp-migration' ), $e->getMessage() ); throw $e; } status_header( $e->getCode() ); ai1wm_json_response( array( 'errors' => array( array( 'code' => $e->getCode(), 'message' => $e->getMessage() ) ) ) ); exit; } catch ( Ai1wm_Database_Exception $e ) { do_action( 'ai1wm_status_import_error', $params, $e ); if ( defined( 'WP_CLI' ) ) { /* translators: 1: Error code, 2: Error message. */ WP_CLI::error( sprintf( __( 'Import failed (database error). Code: %1$s. %2$s', 'all-in-one-wp-migration' ), $e->getCode(), $e->getMessage() ) ); } // REST API: write job-scoped error so poll clients see the terminal state, then re-throw if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { Ai1wm_Status::error( __( 'Import failed', 'all-in-one-wp-migration' ), $e->getMessage() ); throw $e; } status_header( $e->getCode() ); ai1wm_json_response( array( 'errors' => array( array( 'code' => $e->getCode(), 'message' => $e->getMessage() ) ) ) ); exit; } catch ( Ai1wm_Not_Decryptable_Exception $e ) { // Check_Encryption signals pipeline halt; storage/status preserved for retry via /confirm if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { throw $e; } exit; } catch ( Exception $e ) { do_action( 'ai1wm_status_import_error', $params, $e ); if ( defined( 'WP_CLI' ) ) { /* translators: Error message. */ WP_CLI::error( sprintf( __( 'Import failed: %s', 'all-in-one-wp-migration' ), $e->getMessage() ) ); } // Write job-scoped error first so REST poll clients see the terminal state if ( $e instanceof Ai1wm_CRC_Exception ) { Ai1wm_Status::left_error( __( 'Import failed', 'all-in-one-wp-migration' ), $e->getMessage() ); } else { Ai1wm_Status::error( __( 'Import failed', 'all-in-one-wp-migration' ), $e->getMessage() ); } // REST API: re-throw so the handler can return a WP_Error if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { throw $e; } Ai1wm_Notification::error( __( 'Import failed', 'all-in-one-wp-migration' ), $e->getMessage() ); exit; } } // Set completed $completed = true; if ( isset( $params['completed'] ) ) { $completed = (bool) $params['completed']; } // Do request if ( $completed === false || ( $next = next( $filters ) ) && ( $params['priority'] = key( $filters ) ) ) { if ( defined( 'WP_CLI' ) ) { if ( ! defined( 'DOING_CRON' ) ) { continue; } } if ( isset( $params['ai1wm_manual_import'] ) || isset( $params['ai1wm_manual_restore'] ) ) { ai1wm_json_response( $params ); exit; } wp_remote_request( apply_filters( 'ai1wm_http_import_url', add_query_arg( array( 'ai1wm_import' => 1 ), admin_url( 'admin-ajax.php?action=ai1wm_import' ) ) ), array( 'method' => apply_filters( 'ai1wm_http_import_method', 'POST' ), 'timeout' => apply_filters( 'ai1wm_http_import_timeout', 10 ), 'blocking' => apply_filters( 'ai1wm_http_import_blocking', false ), 'sslverify' => apply_filters( 'ai1wm_http_import_sslverify', false ), 'headers' => apply_filters( 'ai1wm_http_import_headers', array() ), 'body' => apply_filters( 'ai1wm_http_import_body', $params ), ) ); // REST API: return params instead of exit so the handler can build a response if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return $params; } exit; } } next( $filters ); } } return $params; } public static function buttons() { $active_filters = array(); $static_filters = array(); // All-in-One WP Migration if ( defined( 'AI1WM_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_file', Ai1wm_Template::get_content( 'import/button-file' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_file', Ai1wm_Template::get_content( 'import/button-file' ) ); } // Add Google Drive Extension if ( defined( 'AI1WMGE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_gdrive', Ai1wm_Template::get_content( 'import/button-gdrive' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_gdrive', Ai1wm_Template::get_content( 'import/button-gdrive' ) ); } // Add FTP Extension if ( defined( 'AI1WMFE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_ftp', Ai1wm_Template::get_content( 'import/button-ftp' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_ftp', Ai1wm_Template::get_content( 'import/button-ftp' ) ); } // Add Dropbox Extension if ( defined( 'AI1WMDE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_dropbox', Ai1wm_Template::get_content( 'import/button-dropbox' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_dropbox', Ai1wm_Template::get_content( 'import/button-dropbox' ) ); } // Add URL Extension if ( defined( 'AI1WMLE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_url', Ai1wm_Template::get_content( 'import/button-url' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_url', Ai1wm_Template::get_content( 'import/button-url' ) ); } // Add Amazon S3 Extension if ( defined( 'AI1WMSE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_s3', Ai1wm_Template::get_content( 'import/button-s3' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_s3', Ai1wm_Template::get_content( 'import/button-s3' ) ); } // Add OneDrive Extension if ( defined( 'AI1WMOE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_onedrive', Ai1wm_Template::get_content( 'import/button-onedrive' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_onedrive', Ai1wm_Template::get_content( 'import/button-onedrive' ) ); } // Add pCloud Extension if ( defined( 'AI1WMPE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_pcloud', Ai1wm_Template::get_content( 'import/button-pcloud' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_pcloud', Ai1wm_Template::get_content( 'import/button-pcloud' ) ); } // Add S3 Client Extension if ( defined( 'AI1WMNE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_s3_client', Ai1wm_Template::get_content( 'import/button-s3-client' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_s3_client', Ai1wm_Template::get_content( 'import/button-s3-client' ) ); } // Add Google Cloud Storage Extension if ( defined( 'AI1WMCE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_gcloud_storage', Ai1wm_Template::get_content( 'import/button-gcloud-storage' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_gcloud_storage', Ai1wm_Template::get_content( 'import/button-gcloud-storage' ) ); } // Add DigitalOcean Spaces Extension if ( defined( 'AI1WMIE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_digitalocean', Ai1wm_Template::get_content( 'import/button-digitalocean' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_digitalocean', Ai1wm_Template::get_content( 'import/button-digitalocean' ) ); } // Add Mega Extension if ( defined( 'AI1WMEE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_mega', Ai1wm_Template::get_content( 'import/button-mega' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_mega', Ai1wm_Template::get_content( 'import/button-mega' ) ); } // Add Backblaze B2 Extension if ( defined( 'AI1WMAE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_b2', Ai1wm_Template::get_content( 'import/button-b2' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_b2', Ai1wm_Template::get_content( 'import/button-b2' ) ); } // Add Box Extension if ( defined( 'AI1WMBE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_box', Ai1wm_Template::get_content( 'import/button-box' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_box', Ai1wm_Template::get_content( 'import/button-box' ) ); } // Add Microsoft Azure Extension if ( defined( 'AI1WMZE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_azure_storage', Ai1wm_Template::get_content( 'import/button-azure-storage' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_azure_storage', Ai1wm_Template::get_content( 'import/button-azure-storage' ) ); } // Add WebDAV Extension if ( defined( 'AI1WMWE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_webdav', Ai1wm_Template::get_content( 'import/button-webdav' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_webdav', Ai1wm_Template::get_content( 'import/button-webdav' ) ); } // Add Amazon Glacier Extension if ( defined( 'AI1WMRE_PLUGIN_NAME' ) ) { $active_filters[] = apply_filters( 'ai1wm_import_glacier', Ai1wm_Template::get_content( 'import/button-glacier' ) ); } else { $static_filters[] = apply_filters( 'ai1wm_import_glacier', Ai1wm_Template::get_content( 'import/button-glacier' ) ); } return array_merge( $active_filters, $static_filters ); } public static function pro() { return Ai1wm_Template::get_content( 'import/pro' ); } public static function max_chunk_size() { return min( ai1wm_parse_size( ini_get( 'post_max_size' ), AI1WM_MAX_CHUNK_SIZE ), ai1wm_parse_size( ini_get( 'upload_max_filesize' ), AI1WM_MAX_CHUNK_SIZE ), ai1wm_parse_size( AI1WM_MAX_CHUNK_SIZE ) ); } } /** * Send Tracks events on behalf of a user using pixel images in page footer. * * @package WooCommerce\Tracks */ defined( 'ABSPATH' ) || exit; /** * WC_Tracks_Footer_Pixel class. */ class WC_Tracks_Footer_Pixel { /** * Singleton instance. * * @var WC_Tracks_Footer_Pixel */ protected static $instance = null; /** * Events to send to Tracks. * * @var array */ protected $events = array(); /** * Instantiate the singleton. * * @return WC_Tracks_Footer_Pixel */ public static function instance() { if ( is_null( self::$instance ) ) { self::$instance = new WC_Tracks_Footer_Pixel(); } return self::$instance; } /** * Constructor - attach hooks to the singleton instance. */ public function __construct() { add_action( 'admin_footer', array( $this, 'render_tracking_pixels' ) ); add_action( 'shutdown', array( $this, 'send_tracks_requests' ) ); } /** * Record a Tracks event * * @param array $event Array of event properties. * @return bool|WP_Error True on success, WP_Error on failure. */ public static function record_event( $event ) { if ( ! $event instanceof WC_Tracks_Event ) { $event = new WC_Tracks_Event( $event ); } if ( is_wp_error( $event ) ) { return $event; } self::instance()->add_event( $event ); return true; } /** * Add a Tracks event to the queue. * * @param WC_Tracks_Event $event Event to track. */ public function add_event( $event ) { $this->events[] = $event; } /** * Add events as tracking pixels to page footer. */ public function render_tracking_pixels() { if ( empty( $this->events ) ) { return; } foreach ( $this->events as $event ) { $pixel = $event->build_pixel_url(); if ( ! $pixel ) { continue; } // Add the Request Timestamp and no cache parameter just before the HTTP request. $pixel = WC_Tracks_Client::add_request_timestamp_and_nocache( $pixel ); echo ''; } $this->events = array(); } /** * Fire off API calls for events that weren't converted to pixels. * * This handles wp_redirect(). */ public function send_tracks_requests() { if ( empty( $this->events ) ) { return; } foreach ( $this->events as $event ) { WC_Tracks_Client::record_event( $event ); } } /** * Get all events. */ public static function get_events() { return self::instance()->events; } /** * Clear all queued events. */ public static function clear_events() { self::instance()->events = array(); } } /** * Template Loader * * @package WooCommerce\Classes */ defined( 'ABSPATH' ) || exit; /** * Template loader class. */ class WC_Template_Loader { /** * Store the shop page ID. * * @var integer */ private static $shop_page_id = 0; /** * Store whether we're processing a product inside the_content filter. * * @var boolean */ private static $in_content_filter = false; /** * Is WooCommerce support defined? * * @var boolean */ private static $theme_support = false; /** * Hook in methods. */ public static function init() { self::$theme_support = wc_current_theme_supports_woocommerce_or_fse(); self::$shop_page_id = wc_get_page_id( 'shop' ); // Supported themes. if ( self::$theme_support ) { add_filter( 'template_include', array( __CLASS__, 'template_loader' ) ); add_filter( 'comments_template', array( __CLASS__, 'comments_template_loader' ) ); // Loads gallery scripts on Product page for FSE themes. if ( wp_is_block_theme() ) { self::add_support_for_product_page_gallery(); } } else { // Unsupported themes. add_action( 'template_redirect', array( __CLASS__, 'unsupported_theme_init' ) ); } } /** * Load a template. * * Handles template usage so that we can use our own templates instead of the theme's. * * Templates are in the 'templates' folder. WooCommerce looks for theme * overrides in /theme/woocommerce/ by default. * * For beginners, it also looks for a woocommerce.php template first. If the user adds * this to the theme (containing a woocommerce() inside) this will be used for all * WooCommerce templates. * * @param string $template Template to load. * @return string */ public static function template_loader( $template ) { if ( is_embed() ) { return $template; } $default_file = self::get_template_loader_default_file(); if ( $default_file ) { /** * Filter hook to choose which files to find before WooCommerce does it's own logic. * * @since 3.0.0 * @var array */ $search_files = self::get_template_loader_files( $default_file ); $template = locate_template( $search_files ); if ( ! $template || WC_TEMPLATE_DEBUG_MODE ) { if ( false !== strpos( $default_file, 'product_cat' ) || false !== strpos( $default_file, 'product_tag' ) ) { $cs_template = str_replace( '_', '-', $default_file ); $template = WC()->plugin_path() . '/templates/' . $cs_template; } else { $template = WC()->plugin_path() . '/templates/' . $default_file; } } } return $template; } /** * Checks whether a block template for a given taxonomy exists. * * **Note:** This checks both the `templates` and `block-templates` directories * as both conventions should be supported. * * @param object $taxonomy Object taxonomy to check. * @return boolean */ private static function taxonomy_has_block_template( $taxonomy ): bool { if ( taxonomy_is_product_attribute( $taxonomy->taxonomy ) ) { $template_name = 'taxonomy-product_attribute'; } else { $template_name = 'taxonomy-' . $taxonomy->taxonomy; } return self::has_block_template( $template_name ); } /** * Checks whether a block template with that name exists. * * **Note: ** This checks both the `templates` and `block-templates` directories * as both conventions should be supported. * * @since 5.5.0 * @param string $template_name Template to check. * @return boolean */ private static function has_block_template( $template_name ) { if ( ! $template_name ) { return false; } $has_template = WP_Block_Templates_Registry::get_instance()->is_registered( 'woocommerce//' . $template_name ); /** * Filters the value of the result of the block template check. * * @since 10.2.0 * * @param boolean $has_template value to be filtered. * @param string $template_name The name of the template. */ return (bool) apply_filters( 'woocommerce_has_block_template', $has_template, $template_name ); } /** * Get the default filename for a template except if a block template with * the same name exists. * * @since 3.0.0 * @since 5.5.0 If a block template with the same name exists, return an * empty string. * @since 6.3.0 It checks custom product taxonomies * @return string */ private static function get_template_loader_default_file() { if ( is_singular( 'product' ) && ! self::has_block_template( 'single-product' ) ) { $default_file = 'single-product.php'; } elseif ( is_product_taxonomy() ) { $object = get_queried_object(); if ( self::taxonomy_has_block_template( $object ) ) { $default_file = ''; } elseif ( taxonomy_is_product_attribute( $object->taxonomy ) ) { $default_file = 'taxonomy-product-attribute.php'; } elseif ( is_tax( 'product_cat' ) || is_tax( 'product_tag' ) ) { $default_file = 'taxonomy-' . $object->taxonomy . '.php'; } elseif ( ! self::has_block_template( 'archive-product' ) ) { $default_file = 'archive-product.php'; } else { $default_file = ''; } } elseif ( ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) && ! self::has_block_template( 'archive-product' ) ) { $default_file = self::$theme_support ? 'archive-product.php' : ''; } else { $default_file = ''; } return $default_file; } /** * Get an array of filenames to search for a given template. * * @since 3.0.0 * @param string $default_file The default file name. * @return string[] */ private static function get_template_loader_files( $default_file ) { $templates = apply_filters( 'woocommerce_template_loader_files', array(), $default_file ); $templates[] = 'woocommerce.php'; if ( is_page_template() ) { $page_template = get_page_template_slug(); if ( $page_template ) { $validated_file = validate_file( $page_template ); if ( 0 === $validated_file ) { $templates[] = $page_template; } else { error_log( "WooCommerce: Unable to validate template path: \"$page_template\". Error Code: $validated_file." ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log } } } if ( is_singular( 'product' ) ) { $object = get_queried_object(); $name_decoded = urldecode( $object->post_name ); if ( $name_decoded !== $object->post_name ) { $templates[] = "single-product-{$name_decoded}.php"; } $templates[] = "single-product-{$object->post_name}.php"; } if ( is_product_taxonomy() ) { $object = get_queried_object(); $templates[] = 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php'; $templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php'; $templates[] = 'taxonomy-' . $object->taxonomy . '.php'; $templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '.php'; if ( taxonomy_is_product_attribute( $object->taxonomy ) ) { $templates[] = 'taxonomy-product_attribute.php'; $templates[] = WC()->template_path() . 'taxonomy-product_attribute.php'; $templates[] = $default_file; } if ( is_tax( 'product_cat' ) || is_tax( 'product_tag' ) ) { $cs_taxonomy = str_replace( '_', '-', $object->taxonomy ); $cs_default = str_replace( '_', '-', $default_file ); $templates[] = 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php'; $templates[] = WC()->template_path() . 'taxonomy-' . $cs_taxonomy . '-' . $object->slug . '.php'; $templates[] = 'taxonomy-' . $object->taxonomy . '.php'; $templates[] = WC()->template_path() . 'taxonomy-' . $cs_taxonomy . '.php'; $templates[] = $cs_default; } } $templates[] = $default_file; if ( isset( $cs_default ) ) { $templates[] = WC()->template_path() . $cs_default; } $templates[] = WC()->template_path() . $default_file; return array_unique( $templates ); } /** * Load comments template. * * @param string $template template to load. * @return string */ public static function comments_template_loader( $template ) { if ( get_post_type() !== 'product' ) { return $template; } $check_dirs = array( trailingslashit( get_stylesheet_directory() ) . WC()->template_path(), trailingslashit( get_template_directory() ) . WC()->template_path(), trailingslashit( get_stylesheet_directory() ), trailingslashit( get_template_directory() ), trailingslashit( WC()->plugin_path() ) . 'templates/', ); if ( WC_TEMPLATE_DEBUG_MODE ) { $check_dirs = array( array_pop( $check_dirs ) ); } foreach ( $check_dirs as $dir ) { if ( file_exists( trailingslashit( $dir ) . 'single-product-reviews.php' ) ) { return trailingslashit( $dir ) . 'single-product-reviews.php'; } } } /** * Unsupported theme compatibility methods. */ /** * Hook in methods to enhance the unsupported theme experience on pages. * * @since 3.3.0 */ public static function unsupported_theme_init() { if ( 0 < self::$shop_page_id ) { if ( is_product_taxonomy() ) { self::unsupported_theme_tax_archive_init(); } elseif ( is_product() ) { self::unsupported_theme_product_page_init(); } else { self::unsupported_theme_shop_page_init(); } } } /** * Hook in methods to enhance the unsupported theme experience on the Shop page. * * @since 3.3.0 */ private static function unsupported_theme_shop_page_init() { add_filter( 'the_content', array( __CLASS__, 'unsupported_theme_shop_content_filter' ), 10 ); add_filter( 'the_title', array( __CLASS__, 'unsupported_theme_title_filter' ), 10, 2 ); add_filter( 'comments_number', array( __CLASS__, 'unsupported_theme_comments_number_filter' ) ); } /** * Hook in methods to enhance the unsupported theme experience on Product pages. * * @since 3.3.0 */ private static function unsupported_theme_product_page_init() { add_filter( 'the_content', array( __CLASS__, 'unsupported_theme_product_content_filter' ), 10 ); add_filter( 'post_thumbnail_html', array( __CLASS__, 'unsupported_theme_single_featured_image_filter' ) ); add_filter( 'woocommerce_product_tabs', array( __CLASS__, 'unsupported_theme_remove_review_tab' ) ); remove_action( 'woocommerce_before_main_content', 'woocommerce_output_content_wrapper', 10 ); remove_action( 'woocommerce_after_main_content', 'woocommerce_output_content_wrapper_end', 10 ); self::add_support_for_product_page_gallery(); } /** * Add theme support for Product page gallery. * * @since x.x.x */ private static function add_support_for_product_page_gallery() { add_theme_support( 'wc-product-gallery-zoom' ); add_theme_support( 'wc-product-gallery-lightbox' ); add_theme_support( 'wc-product-gallery-slider' ); } /** * Enhance the unsupported theme experience on Product Category and Attribute pages by rendering * those pages using the single template and shortcode-based content. To do this we make a dummy * post and set a shortcode as the post content. This approach is adapted from bbPress. * * @since 3.3.0 */ private static function unsupported_theme_tax_archive_init() { global $wp_query, $post; $queried_object = get_queried_object(); $args = self::get_current_shop_view_args(); $shortcode_args = array( 'page' => $args->page, 'columns' => $args->columns, 'rows' => $args->rows, 'orderby' => '', 'order' => '', 'paginate' => true, 'cache' => false, ); if ( is_product_category() ) { $shortcode_args['category'] = sanitize_title( $queried_object->slug ); } elseif ( taxonomy_is_product_attribute( $queried_object->taxonomy ) ) { $shortcode_args['attribute'] = sanitize_title( $queried_object->taxonomy ); $shortcode_args['terms'] = sanitize_title( $queried_object->slug ); } elseif ( is_product_tag() ) { $shortcode_args['tag'] = sanitize_title( $queried_object->slug ); } else { // Default theme archive for all other taxonomies. return; } // Description handling. if ( ! empty( $queried_object->description ) && ( empty( $_GET['product-page'] ) || 1 === absint( $_GET['product-page'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $prefix = '
' . wc_format_content( wp_kses_post( $queried_object->description ) ) . '
'; } else { $prefix = ''; } add_filter( 'woocommerce_shortcode_products_query', array( __CLASS__, 'unsupported_archive_layered_nav_compatibility' ) ); $shortcode = new WC_Shortcode_Products( $shortcode_args ); remove_filter( 'woocommerce_shortcode_products_query', array( __CLASS__, 'unsupported_archive_layered_nav_compatibility' ) ); $shop_page = get_post( self::$shop_page_id ); $dummy_post_properties = array( 'ID' => 0, 'post_status' => 'publish', 'post_author' => $shop_page->post_author, 'post_parent' => 0, 'post_type' => 'page', 'post_date' => $shop_page->post_date, 'post_date_gmt' => $shop_page->post_date_gmt, 'post_modified' => $shop_page->post_modified, 'post_modified_gmt' => $shop_page->post_modified_gmt, 'post_content' => $prefix . $shortcode->get_content(), 'post_title' => wc_clean( $queried_object->name ), 'post_excerpt' => '', 'post_content_filtered' => '', 'post_mime_type' => '', 'post_password' => '', 'post_name' => $queried_object->slug, 'guid' => '', 'menu_order' => 0, 'pinged' => '', 'to_ping' => '', 'ping_status' => '', 'comment_status' => 'closed', 'comment_count' => 0, 'filter' => 'raw', ); // Set the $post global. $post = new WP_Post( (object) $dummy_post_properties ); // @codingStandardsIgnoreLine. // Copy the new post global into the main $wp_query. $wp_query->post = $post; $wp_query->posts = array( $post ); // Prevent comments form from appearing. $wp_query->post_count = 1; $wp_query->is_404 = false; $wp_query->is_page = true; $wp_query->is_single = true; $wp_query->is_archive = false; $wp_query->is_tax = true; $wp_query->max_num_pages = 0; // Prepare everything for rendering. setup_postdata( $post ); remove_all_filters( 'the_content' ); remove_all_filters( 'the_excerpt' ); add_filter( 'template_include', array( __CLASS__, 'force_single_template_filter' ) ); } /** * Add layered nav args to WP_Query args generated by the 'products' shortcode. * * @since 3.3.4 * @param array $query WP_Query args. * @return array */ public static function unsupported_archive_layered_nav_compatibility( $query ) { foreach ( WC()->query->get_layered_nav_chosen_attributes() as $taxonomy => $data ) { $query['tax_query'][] = array( 'taxonomy' => $taxonomy, 'field' => 'slug', 'terms' => $data['terms'], 'operator' => 'and' === $data['query_type'] ? 'AND' : 'IN', 'include_children' => false, ); } return $query; } /** * Force the loading of one of the single templates instead of whatever template was about to be loaded. * * @since 3.3.0 * @param string $template Path to template. * @return string */ public static function force_single_template_filter( $template ) { $possible_templates = array( 'page', 'single', 'singular', 'index', ); foreach ( $possible_templates as $possible_template ) { $path = get_query_template( $possible_template ); if ( $path ) { return $path; } } return $template; } /** * Get information about the current shop page view. * * @since 3.3.0 * @return array */ private static function get_current_shop_view_args() { return (object) array( 'page' => absint( max( 1, absint( get_query_var( 'paged' ) ) ) ), 'columns' => wc_get_default_products_per_row(), 'rows' => wc_get_default_product_rows_per_page(), ); } /** * Filter the title and insert WooCommerce content on the shop page. * * For non-WC themes, this will setup the main shop page to be shortcode based to improve default appearance. * * @since 3.3.0 * @param string $title Existing title. * @param int|null $id ID of the post being filtered. * @return string */ public static function unsupported_theme_title_filter( $title, $id = null ) { if ( is_null( $id ) || self::$theme_support || ! $id !== self::$shop_page_id ) { return $title; } if ( is_page( self::$shop_page_id ) || ( is_home() && 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ) === self::$shop_page_id ) ) { $args = self::get_current_shop_view_args(); $title_suffix = array(); if ( $args->page > 1 ) { /* translators: %d: Page number. */ $title_suffix[] = sprintf( esc_html__( 'Page %d', 'woocommerce' ), $args->page ); } if ( $title_suffix ) { $title = $title . ' – ' . implode( ', ', $title_suffix ); } } return $title; } /** * Filter the content and insert WooCommerce content on the shop page. * * For non-WC themes, this will setup the main shop page to be shortcode based to improve default appearance. * * @since 3.3.0 * @param string $content Existing post content. * @return string */ public static function unsupported_theme_shop_content_filter( $content ) { global $wp_query; if ( self::$theme_support || ! is_main_query() || ! in_the_loop() ) { return $content; } self::$in_content_filter = true; // Remove the filter we're in to avoid nested calls. remove_filter( 'the_content', array( __CLASS__, 'unsupported_theme_shop_content_filter' ) ); // Unsupported theme shop page. if ( is_page( self::$shop_page_id ) ) { $args = self::get_current_shop_view_args(); $shortcode = new WC_Shortcode_Products( array_merge( WC()->query->get_catalog_ordering_args(), array( 'page' => $args->page, 'columns' => $args->columns, 'rows' => $args->rows, 'orderby' => '', 'order' => '', 'paginate' => true, 'cache' => false, ) ), 'products' ); // Allow queries to run e.g. layered nav. add_action( 'pre_get_posts', array( WC()->query, 'product_query' ) ); $content = $content . $shortcode->get_content(); // Remove actions and self to avoid nested calls. remove_action( 'pre_get_posts', array( WC()->query, 'product_query' ) ); WC()->query->remove_ordering_args(); } self::$in_content_filter = false; return $content; } /** * Filter the content and insert WooCommerce content on the shop page. * * For non-WC themes, this will setup the main shop page to be shortcode based to improve default appearance. * * @since 3.3.0 * @param string $content Existing post content. * @return string */ public static function unsupported_theme_product_content_filter( $content ) { global $wp_query; if ( self::$theme_support || ! is_main_query() || ! in_the_loop() ) { return $content; } self::$in_content_filter = true; // Remove the filter we're in to avoid nested calls. remove_filter( 'the_content', array( __CLASS__, 'unsupported_theme_product_content_filter' ) ); if ( is_product() ) { $content = do_shortcode( '[product_page id="' . get_the_ID() . '" show_title=0 status="any"]' ); } self::$in_content_filter = false; return $content; } /** * Suppress the comments number on the Shop page for unsupported themes since there is no commenting on the Shop page. * * @since 3.4.5 * @param string $comments_number The comments number text. * @return string */ public static function unsupported_theme_comments_number_filter( $comments_number ) { if ( is_page( self::$shop_page_id ) ) { return ''; } return $comments_number; } /** * Are we filtering content for unsupported themes? * * @since 3.3.2 * @return bool */ public static function in_content_filter() { return (bool) self::$in_content_filter; } /** * Prevent the main featured image on product pages because there will be another featured image * in the gallery. * * @since 3.3.0 * @param string $html Img element HTML. * @return string */ public static function unsupported_theme_single_featured_image_filter( $html ) { if ( self::in_content_filter() || ! is_product() || ! is_main_query() ) { return $html; } return ''; } /** * Remove the Review tab and just use the regular comment form. * * @param array $tabs Tab info. * @return array */ public static function unsupported_theme_remove_review_tab( $tabs ) { unset( $tabs['reviews'] ); return $tabs; } } add_action( 'init', array( 'WC_Template_Loader', 'init' ) ); /** * The Akismet integration module * * @link https://akismet.com/development/api/ */ wpcf7_include_module_file( 'akismet/service.php' ); add_action( 'wpcf7_init', 'wpcf7_akismet_register_service', 30, 0 ); /** * Registers the Akismet service. */ function wpcf7_akismet_register_service() { $integration = WPCF7_Integration::get_instance(); $integration->add_service( 'akismet', WPCF7_Akismet::get_instance() ); } add_filter( 'wpcf7_spam', 'wpcf7_akismet', 10, 2 ); function wpcf7_akismet( $spam, $submission ) { if ( $spam ) { return $spam; } if ( ! wpcf7_akismet_is_available() ) { return false; } if ( ! $params = wpcf7_akismet_submitted_params() ) { return false; } $comment = array( 'comment_type' => 'contact-form', 'comment_author' => $params['author'], 'comment_author_email' => $params['author_email'], 'comment_author_url' => $params['author_url'], 'comment_content' => $params['content'], 'blog' => home_url(), 'blog_lang' => get_locale(), 'blog_charset' => get_option( 'blog_charset' ), 'user_ip' => $submission->get_meta( 'remote_ip' ), 'user_agent' => $submission->get_meta( 'user_agent' ), 'referrer' => wpcf7_superglobal_server( 'HTTP_REFERER' ), ); $datetime = date_create_immutable( '@' . $submission->get_meta( 'timestamp' ) ); if ( $datetime ) { $comment['comment_date_gmt'] = $datetime->format( DATE_ATOM ); } if ( $permalink = get_permalink() ) { $comment['permalink'] = $permalink; } $server_vars = array_diff_key( $_SERVER, array_flip( array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ) ) ); $comment = array_merge( $comment, $server_vars ); $comment = apply_filters( 'wpcf7_akismet_parameters', $comment ); if ( wpcf7_akismet_comment_check( $comment ) ) { $spam = true; $submission->add_spam_log( array( 'agent' => 'akismet', 'reason' => __( 'Akismet returns a spam response.', 'contact-form-7' ), ) ); } else { $spam = false; } return $spam; } /** * Returns true if Akismet is active and has a valid API key. */ function wpcf7_akismet_is_available() { if ( is_callable( array( 'Akismet', 'get_api_key' ) ) ) { return (bool) Akismet::get_api_key(); } return false; } /** * Returns an array of parameters based on the current form submission. * Returns false if Akismet is not active on the contact form. */ function wpcf7_akismet_submitted_params() { $akismet_tags = array_filter( wpcf7_scan_form_tags(), static function ( $tag ) { $akismet_option = $tag->get_option( 'akismet', '(author|author_email|author_url)', true ); return (bool) $akismet_option; } ); if ( ! $akismet_tags ) { // Akismet is not active on this contact form. return false; } $params = array( 'author' => '', 'author_email' => '', 'author_url' => '', 'content' => '', ); foreach ( (array) $_POST as $key => $val ) { if ( str_starts_with( $key, '_wpcf7' ) or '_wpnonce' === $key ) { continue; } $vals = array_filter( wpcf7_array_flatten( $val ), static function ( $val ) { return '' !== trim( $val ); } ); if ( empty( $vals ) ) { continue; } if ( $tags = wpcf7_scan_form_tags( array( 'name' => $key ) ) ) { $tag = $tags[0]; $akismet_option = $tag->get_option( 'akismet', '(author|author_email|author_url)', true ); if ( 'author' === $akismet_option ) { $params['author'] = sprintf( '%s %s', $params['author'], implode( ' ', $vals ) ); continue; } if ( 'author_email' === $akismet_option and '' === $params['author_email'] ) { $params['author_email'] = $vals[0]; continue; } if ( 'author_url' === $akismet_option and '' === $params['author_url'] ) { $params['author_url'] = $vals[0]; continue; } $vals = array_filter( $vals, static function ( $val ) use ( $tag ) { if ( wpcf7_form_tag_supports( $tag->type, 'selectable-values' ) and in_array( $val, $tag->labels, true ) ) { return false; } else { return true; } } ); } if ( $vals ) { $params['content'] .= "\n\n" . implode( ', ', $vals ); } } $params = array_map( 'trim', $params ); return $params; } /** * Sends data to Akismet. * * @param array $comment Submission and environment data. * @return bool True if Akismet called it spam, or false otherwise. */ function wpcf7_akismet_comment_check( $comment ) { $spam = false; $query_string = wpcf7_build_query( $comment ); if ( is_callable( array( 'Akismet', 'http_post' ) ) ) { $response = Akismet::http_post( $query_string, 'comment-check' ); } else { return $spam; } if ( 'true' === $response[1] ) { $spam = true; } if ( $submission = WPCF7_Submission::get_instance() ) { $submission->push( 'akismet', array( 'comment' => $comment, 'spam' => $spam, ) ); } return apply_filters( 'wpcf7_akismet_comment_check', $spam, $comment ); } add_filter( 'wpcf7_posted_data', 'wpcf7_akismet_posted_data', 10, 1 ); /** * Removes Akismet-related properties from the posted data. * * This does not affect the $_POST variable itself. * * @link https://plugins.trac.wordpress.org/browser/akismet/tags/5.0/_inc/akismet-frontend.js */ function wpcf7_akismet_posted_data( $posted_data ) { if ( wpcf7_akismet_is_available() ) { $posted_data = array_diff_key( $posted_data, array( 'ak_bib' => '', 'ak_bfs' => '', 'ak_bkpc' => '', 'ak_bkp' => '', 'ak_bmc' => '', 'ak_bmcc' => '', 'ak_bmk' => '', 'ak_bck' => '', 'ak_bmmc' => '', 'ak_btmc' => '', 'ak_bsc' => '', 'ak_bte' => '', 'ak_btec' => '', 'ak_bmm' => '', ) ); } return $posted_data; } add_filter( 'wpcf7_default_template', 'wpcf7_akismet_default_template', 10, 2 ); function wpcf7_akismet_default_template( $template, $prop ) { if ( ! wpcf7_akismet_is_available() ) { return $template; } if ( 'form' === $prop ) { $template = str_replace( array( '[text* your-name ', '[email* your-email ', ), array( '[text* your-name akismet:author ', '[email* your-email akismet:author_email ', ), $template ); $privacy_notice = sprintf( '%s %s', __( 'This form uses Akismet to reduce spam.', 'contact-form-7' ), wpcf7_link( 'https://akismet.com/privacy/', __( 'Learn how your data is processed.', 'contact-form-7' ), array( 'target' => '_blank', 'rel' => 'nofollow noopener', ) ) ); $template .= "\n\n" . $privacy_notice; } return $template; } namespace Automattic\WooCommerce\Blocks\BlockTypes; /** * CheckoutOrderSummaryCouponFormBlock class. */ class CheckoutOrderSummaryCouponFormBlock extends AbstractInnerBlock { /** * Block name. * * @var string */ protected $block_name = 'checkout-order-summary-coupon-form-block'; }