Файловый менеджер - Редактировать - /home/tuudkjt/globeasy/wp-includes/ID3/Blocks.zip
Назад
PK SO.]r�y3G 3G QueryFilters.phpnu ��� <?php declare( strict_types = 1 ); namespace Automattic\WooCommerce\Blocks; use WC_Tax; use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore; /** * Process the query data for filtering purposes. */ final class QueryFilters { /** * Initialization method. * * @internal */ public function init() {} /** * Filter the posts clauses of the main query to support global filters. * * @param array $args Query args. * @param \WP_Query $wp_query WP_Query object. * @return array */ public function main_query_filter( $args, $wp_query ) { if ( ! $wp_query->is_main_query() || 'product_query' !== $wp_query->get( 'wc_query' ) ) { return $args; } if ( $wp_query->get( 'filter_stock_status' ) ) { $args = $this->stock_filter_clauses( $args, $wp_query ); } return $args; } /** * Add conditional query clauses based on the filter params in query vars. * * @param array $args Query args. * @param \WP_Query $wp_query WP_Query object. * @return array */ public function add_query_clauses( $args, $wp_query ) { $args = $this->stock_filter_clauses( $args, $wp_query ); $args = $this->price_filter_clauses( $args, $wp_query ); $args = $this->attribute_filter_clauses( $args, $wp_query ); return $args; } /** * Get price data for current products. * * @param array $query_vars The WP_Query arguments. * @return object */ public function get_filtered_price( $query_vars ) { global $wpdb; add_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10, 2 ); add_filter( 'posts_pre_query', '__return_empty_array' ); $query_vars['no_found_rows'] = true; $query_vars['posts_per_page'] = -1; $query_vars['fields'] = 'ids'; $query = new \WP_Query(); $query->query( $query_vars ); $product_query_sql = $query->request; remove_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10 ); remove_filter( 'posts_pre_query', '__return_empty_array' ); $price_filter_sql = " SELECT min( min_price ) as min_price, MAX( max_price ) as max_price FROM {$wpdb->wc_product_meta_lookup} WHERE product_id IN ( {$product_query_sql} ) "; return $wpdb->get_row( $price_filter_sql ); // phpcs:ignore } /** * Get stock status counts for the current products. * * @param array $query_vars The WP_Query arguments. * @return array status=>count pairs. */ public function get_stock_status_counts( $query_vars ) { global $wpdb; $stock_status_options = array_map( 'esc_sql', array_keys( wc_get_product_stock_status_options() ) ); add_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10, 2 ); add_filter( 'posts_pre_query', '__return_empty_array' ); $query_vars['no_found_rows'] = true; $query_vars['posts_per_page'] = -1; $query_vars['fields'] = 'ids'; $query = new \WP_Query(); $result = $query->query( $query_vars ); $product_query_sql = $query->request; remove_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10 ); remove_filter( 'posts_pre_query', '__return_empty_array' ); $stock_status_counts = array(); foreach ( $stock_status_options as $status ) { $stock_status_count_sql = $this->generate_stock_status_count_query( $status, $product_query_sql, $stock_status_options ); $result = $wpdb->get_row( $stock_status_count_sql ); // phpcs:ignore $stock_status_counts[ $status ] = $result->status_count; } return $stock_status_counts; } /** * Get rating counts for the current products. * * @param array $query_vars The WP_Query arguments. * @return array rating=>count pairs. */ public function get_rating_counts( $query_vars ) { global $wpdb; add_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10, 2 ); add_filter( 'posts_pre_query', '__return_empty_array' ); $query_vars['no_found_rows'] = true; $query_vars['posts_per_page'] = -1; $query_vars['fields'] = 'ids'; $query = new \WP_Query(); $query->query( $query_vars ); $product_query_sql = $query->request; remove_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10 ); remove_filter( 'posts_pre_query', '__return_empty_array' ); $rating_count_sql = " SELECT COUNT( DISTINCT product_id ) as product_count, ROUND( average_rating, 0 ) as rounded_average_rating FROM {$wpdb->wc_product_meta_lookup} WHERE product_id IN ( {$product_query_sql} ) AND average_rating > 0 GROUP BY rounded_average_rating ORDER BY rounded_average_rating DESC "; $results = $wpdb->get_results( $rating_count_sql ); // phpcs:ignore return array_map( 'absint', wp_list_pluck( $results, 'product_count', 'rounded_average_rating' ) ); } /** * Get attribute counts for the current products. * * @param array $query_vars The WP_Query arguments. * @param string $attribute_to_count Attribute taxonomy name. * @return array termId=>count pairs. */ public function get_attribute_counts( $query_vars, $attribute_to_count ) { global $wpdb; add_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10, 2 ); add_filter( 'posts_pre_query', '__return_empty_array' ); $query_vars['no_found_rows'] = true; $query_vars['posts_per_page'] = -1; $query_vars['fields'] = 'ids'; $query = new \WP_Query(); $result = $query->query( $query_vars ); $product_query_sql = $query->request; remove_filter( 'posts_clauses', array( $this, 'add_query_clauses' ), 10 ); remove_filter( 'posts_pre_query', '__return_empty_array' ); $attributes_to_count = esc_sql( wc_sanitize_taxonomy_name( $attribute_to_count ) ); $attribute_count_sql = "SELECT COUNT(DISTINCT posts.ID) as term_count, terms.term_id as term_count_id FROM {$wpdb->posts} AS posts INNER JOIN {$wpdb->term_relationships} AS term_relationships ON posts.ID = term_relationships.object_id INNER JOIN {$wpdb->term_taxonomy} AS term_taxonomy ON term_relationships.term_taxonomy_id = term_taxonomy.term_taxonomy_id INNER JOIN {$wpdb->terms} AS terms ON term_taxonomy.term_id = terms.term_id WHERE posts.ID IN ( {$product_query_sql} ) AND term_taxonomy.taxonomy IN ('{$attributes_to_count}') AND posts.post_status = 'publish' AND posts.post_type = 'product' GROUP BY terms.term_id ORDER BY terms.name ASC"; $results = $wpdb->get_results( $attribute_count_sql ); // phpcs:ignore return array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) ); } /** * Add query clauses for stock filter. * * @param array $args Query args. * @param \WP_Query $wp_query WP_Query object. * @return array */ private function stock_filter_clauses( $args, $wp_query ) { if ( ! $wp_query->get( 'filter_stock_status' ) ) { return $args; } $args['join'] = $this->append_product_sorting_table_join( $args['join'] ); $args['where'] .= ' AND wc_product_meta_lookup.stock_status IN (\'' . implode( '\',\'', array_map( 'esc_sql', explode( ',', $wp_query->get( 'filter_stock_status' ) ) ) ) . '\')'; return $args; } /** * Add query clauses for price filter. * * @param array $args Query args. * @param \WP_Query $wp_query WP_Query object. * @return array */ private function price_filter_clauses( $args, $wp_query ) { if ( ! $wp_query->get( 'min_price' ) && ! $wp_query->get( 'max_price' ) ) { return $args; } global $wpdb; $adjust_for_taxes = $this->adjust_price_filters_for_displayed_taxes(); $args['join'] = $this->append_product_sorting_table_join( $args['join'] ); if ( $wp_query->get( 'min_price' ) ) { $min_price_filter = intval( $wp_query->get( 'min_price' ) ); if ( $adjust_for_taxes ) { $args['where'] .= $this->get_price_filter_query_for_displayed_taxes( $min_price_filter, 'max_price', '>=' ); } else { $args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.max_price >= %f ', $min_price_filter ); } } if ( $wp_query->get( 'max_price' ) ) { $max_price_filter = intval( $wp_query->get( 'max_price' ) ); if ( $adjust_for_taxes ) { $args['where'] .= $this->get_price_filter_query_for_displayed_taxes( $max_price_filter, 'min_price', '<=' ); } else { $args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.min_price <= %f ', $max_price_filter ); } } return $args; } /** * Join wc_product_meta_lookup to posts if not already joined. * * @param string $sql SQL join. * @return string */ private function append_product_sorting_table_join( $sql ) { global $wpdb; if ( ! strstr( $sql, 'wc_product_meta_lookup' ) ) { $sql .= " LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON $wpdb->posts.ID = wc_product_meta_lookup.product_id "; } return $sql; } /** * Generate calculate query by stock status. * * @param string $status status to calculate. * @param string $product_query_sql product query for current filter state. * @param array $stock_status_options available stock status options. * * @return false|string */ private function generate_stock_status_count_query( $status, $product_query_sql, $stock_status_options ) { if ( ! in_array( $status, $stock_status_options, true ) ) { return false; } global $wpdb; $status = esc_sql( $status ); return " SELECT COUNT( DISTINCT posts.ID ) as status_count FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id AND postmeta.meta_key = '_stock_status' AND postmeta.meta_value = '{$status}' WHERE posts.ID IN ( {$product_query_sql} ) "; } /** * Get query for price filters when dealing with displayed taxes. * * @param float $price_filter Price filter to apply. * @param string $column Price being filtered (min or max). * @param string $operator Comparison operator for column. * @return string Constructed query. */ private function get_price_filter_query_for_displayed_taxes( $price_filter, $column = 'min_price', $operator = '>=' ) { global $wpdb; // Select only used tax classes to avoid unwanted calculations. $product_tax_classes = array_filter( $wpdb->get_col( "SELECT DISTINCT tax_class FROM {$wpdb->wc_product_meta_lookup};" ) ); if ( empty( $product_tax_classes ) ) { return ''; } $or_queries = array(); // We need to adjust the filter for each possible tax class and combine the queries into one. foreach ( $product_tax_classes as $tax_class ) { $adjusted_price_filter = $this->adjust_price_filter_for_tax_class( $price_filter, $tax_class ); $or_queries[] = $wpdb->prepare( '( wc_product_meta_lookup.tax_class = %s AND wc_product_meta_lookup.`' . esc_sql( $column ) . '` ' . esc_sql( $operator ) . ' %f )', $tax_class, $adjusted_price_filter ); } // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared return $wpdb->prepare( ' AND ( wc_product_meta_lookup.tax_status = "taxable" AND ( 0=1 OR ' . implode( ' OR ', $or_queries ) . ') OR ( wc_product_meta_lookup.tax_status != "taxable" AND wc_product_meta_lookup.`' . esc_sql( $column ) . '` ' . esc_sql( $operator ) . ' %f ) ) ', $price_filter ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared } /** * If price filters need adjustment to work with displayed taxes, this returns true. * * This logic is used when prices are stored in the database differently to how they are being displayed, with regards * to taxes. * * @return boolean */ private function adjust_price_filters_for_displayed_taxes() { $display = get_option( 'woocommerce_tax_display_shop' ); $database = wc_prices_include_tax() ? 'incl' : 'excl'; return $display !== $database; } /** * Adjusts a price filter based on a tax class and whether or not the amount includes or excludes taxes. * * This calculation logic is based on `wc_get_price_excluding_tax` and `wc_get_price_including_tax` in core. * * @param float $price_filter Price filter amount as entered. * @param string $tax_class Tax class for adjustment. * @return float */ private function adjust_price_filter_for_tax_class( $price_filter, $tax_class ) { $tax_display = get_option( 'woocommerce_tax_display_shop' ); $tax_rates = WC_Tax::get_rates( $tax_class ); $base_tax_rates = WC_Tax::get_base_tax_rates( $tax_class ); // If prices are shown incl. tax, we want to remove the taxes from the filter amount to match prices stored excl. tax. if ( 'incl' === $tax_display ) { /** * Filters if taxes should be removed from locations outside the store base location. * * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing * with out of base locations. e.g. If a product costs 10 including tax, all users will pay 10 * regardless of location and taxes. * * @since 2.6.0 * * @internal Matches filter name in WooCommerce core. * * @param boolean $adjust_non_base_location_prices True by default. * @return boolean */ $taxes = apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ? WC_Tax::calc_tax( $price_filter, $base_tax_rates, true ) : WC_Tax::calc_tax( $price_filter, $tax_rates, true ); return $price_filter - array_sum( $taxes ); } // If prices are shown excl. tax, add taxes to match the prices stored in the DB. $taxes = WC_Tax::calc_tax( $price_filter, $tax_rates, false ); return $price_filter + array_sum( $taxes ); } /** * Get attribute lookup table name. * * @return string */ private function get_lookup_table_name() { return wc_get_container()->get( LookupDataStore::class )->get_lookup_table_name(); } /** * Add query clauses for attribute filter. * * @param array $args Query args. * @param \WP_Query $wp_query WP_Query object. * @return array */ private function attribute_filter_clauses( $args, $wp_query ) { $chosen_attributes = $this->get_chosen_attributes( $wp_query->query_vars ); if ( empty( $chosen_attributes ) ) { return $args; } global $wpdb; // The extra derived table ("SELECT product_or_parent_id FROM") is needed for performance // (causes the filtering subquery to be executed only once). $clause_root = " {$wpdb->posts}.ID IN ( SELECT product_or_parent_id FROM ("; if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) { $in_stock_clause = ' AND in_stock = 1'; } else { $in_stock_clause = ''; } $attribute_ids_for_and_filtering = array(); foreach ( $chosen_attributes as $taxonomy => $data ) { $all_terms = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false, ) ); $term_ids_by_slug = wp_list_pluck( $all_terms, 'term_id', 'slug' ); $term_ids_to_filter_by = array_values( array_intersect_key( $term_ids_by_slug, array_flip( $data['terms'] ) ) ); $term_ids_to_filter_by = array_map( 'absint', $term_ids_to_filter_by ); $term_ids_to_filter_by_list = '(' . join( ',', $term_ids_to_filter_by ) . ')'; $is_and_query = 'and' === $data['query_type']; $count = count( $term_ids_to_filter_by ); if ( 0 !== $count ) { if ( $is_and_query && $count > 1 ) { $attribute_ids_for_and_filtering = array_merge( $attribute_ids_for_and_filtering, $term_ids_to_filter_by ); } else { $clauses[] = " {$clause_root} SELECT product_or_parent_id FROM {$this->get_lookup_table_name()} lt WHERE term_id in {$term_ids_to_filter_by_list} {$in_stock_clause} )"; } } } if ( ! empty( $attribute_ids_for_and_filtering ) ) { $count = count( $attribute_ids_for_and_filtering ); $term_ids_to_filter_by_list = '(' . join( ',', $attribute_ids_for_and_filtering ) . ')'; $clauses[] = " {$clause_root} SELECT product_or_parent_id FROM {$this->get_lookup_table_name()} lt WHERE is_variation_attribute=0 {$in_stock_clause} AND term_id in {$term_ids_to_filter_by_list} GROUP BY product_id HAVING COUNT(product_id)={$count} UNION SELECT product_or_parent_id FROM {$this->get_lookup_table_name()} lt WHERE is_variation_attribute=1 {$in_stock_clause} AND term_id in {$term_ids_to_filter_by_list} )"; } if ( ! empty( $clauses ) ) { // "temp" is needed because the extra derived tables require an alias. $args['where'] .= ' AND (' . join( ' temp ) AND ', $clauses ) . ' temp ))'; } elseif ( ! empty( $chosen_attributes ) ) { $args['where'] .= ' AND 1=0'; } return $args; } /** * Get an array of attributes and terms selected from query arguments. * * @param array $query_vars The WP_Query arguments. * @return array */ private function get_chosen_attributes( $query_vars ) { $chosen_attributes = array(); if ( empty( $query_vars ) ) { return $chosen_attributes; } foreach ( $query_vars as $key => $value ) { if ( 0 === strpos( $key, 'filter_' ) ) { if ( ! is_string( $value ) ) { continue; } $attribute = wc_sanitize_taxonomy_name( str_replace( 'filter_', '', $key ) ); $taxonomy = wc_attribute_taxonomy_name( $attribute ); $filter_terms = ! empty( $value ) ? explode( ',', wc_clean( wp_unslash( $value ) ) ) : array(); if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) || ! wc_attribute_taxonomy_id_by_name( $attribute ) ) { continue; } $query_type = ! empty( $query_vars[ 'query_type_' . $attribute ] ) && in_array( $query_vars[ 'query_type_' . $attribute ], array( 'and', 'or' ), true ) ? wc_clean( wp_unslash( $query_vars[ 'query_type_' . $attribute ] ) ) : ''; $chosen_attributes[ $taxonomy ]['terms'] = array_map( 'sanitize_title', $filter_terms ); // Ensures correct encoding. $chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : 'and'; } } return $chosen_attributes; } } PK SO.]3�{~dQ dQ Shipping/ShippingController.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\Shipping; use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi; use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry; use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils; use Automattic\WooCommerce\Enums\ProductTaxStatus; use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils; use Automattic\WooCommerce\Utilities\ArrayUtil; use WC_Customer; use WC_Shipping_Rate; use WC_Tracks; /** * ShippingController class. * * @internal */ class ShippingController { /** * Script handle used for enqueueing the scripts needed for managing the Local Pickup Shipping Settings. */ private const LOCAL_PICKUP_ADMIN_JS_HANDLE = 'wc-shipping-method-pickup-location'; /** * Instance of the asset API. * * @var AssetApi */ protected $asset_api; /** * Instance of the asset data registry. * * @var AssetDataRegistry */ protected $asset_data_registry; /** * Whether local pickup is enabled. * * @var bool */ private $local_pickup_enabled; /** * Constructor. * * @param AssetApi $asset_api Instance of the asset API. * @param AssetDataRegistry $asset_data_registry Instance of the asset data registry. */ public function __construct( AssetApi $asset_api, AssetDataRegistry $asset_data_registry ) { $this->asset_api = $asset_api; $this->asset_data_registry = $asset_data_registry; $this->local_pickup_enabled = LocalPickupUtils::is_local_pickup_enabled(); } /** * Initialization method. */ public function init() { if ( is_admin() ) { $this->asset_data_registry->add( 'countryStates', function () { return WC()->countries->get_states(); } ); } $this->asset_data_registry->add( 'shippingCostRequiresAddress', get_option( 'woocommerce_shipping_cost_requires_address', false ) === 'yes' ); add_action( 'rest_api_init', array( $this, 'register_settings' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) ); add_action( 'admin_footer', array( $this, 'hydrate_client_settings' ), 0 ); add_action( 'woocommerce_load_shipping_methods', array( $this, 'register_local_pickup' ) ); add_filter( 'woocommerce_local_pickup_methods', array( $this, 'register_local_pickup_method' ) ); add_filter( 'woocommerce_order_hide_shipping_address', array( $this, 'hide_shipping_address_for_local_pickup' ), 10 ); add_filter( 'woocommerce_customer_taxable_address', array( $this, 'filter_taxable_address' ) ); add_filter( 'woocommerce_shipping_settings', array( $this, 'remove_shipping_settings' ) ); add_filter( 'woocommerce_shipping_packages', array( $this, 'filter_shipping_packages' ) ); add_filter( 'pre_update_option_woocommerce_pickup_location_settings', array( $this, 'flush_cache' ) ); add_filter( 'pre_update_option_pickup_location_pickup_locations', array( $this, 'flush_cache' ) ); add_filter( 'woocommerce_shipping_packages', array( $this, 'remove_shipping_if_no_address' ), 11 ); add_filter( 'woocommerce_order_shipping_to_display', array( $this, 'show_local_pickup_details' ), 10, 2 ); add_action( 'rest_pre_serve_request', array( $this, 'track_local_pickup' ), 10, 4 ); } /** * Inject collection details onto the order received page. * * @param string $return_value Return value. * @param \WC_Order $order Order object. * @return string */ public function show_local_pickup_details( $return_value, $order ) { // Confirm order is valid before proceeding further. if ( ! $order instanceof \WC_Order ) { return $return_value; } $shipping_method_ids = ArrayUtil::select( $order->get_shipping_methods(), 'get_method_id', ArrayUtil::SELECT_BY_OBJECT_METHOD ); $shipping_method_id = current( $shipping_method_ids ); // Ensure order used pickup location method, otherwise bail. if ( 'pickup_location' !== $shipping_method_id ) { return $return_value; } $shipping_method = current( $order->get_shipping_methods() ); $details = $shipping_method->get_meta( 'pickup_details' ); $location = $shipping_method->get_meta( 'pickup_location' ); $address = $shipping_method->get_meta( 'pickup_address' ); $cost = $shipping_method->get_total(); $lines = array(); if ( $location ) { $lines[] = sprintf( // Translators: %s location name. __( 'Collection from <strong>%s</strong>:', 'woocommerce' ), $location ); } if ( $address ) { $lines[] = nl2br( esc_html( str_replace( ',', ', ', $address ) ) ); } if ( $details ) { $lines[] = wp_kses_post( $details ); } if ( $cost > 0 ) { $tax_display = get_option( 'woocommerce_tax_display_cart' ); $tax = $shipping_method->get_total_tax(); // Format cost with tax handling. if ( 'excl' === $tax_display ) { // Show pickup cost excluding tax. $formatted_cost = wc_price( $cost, array( 'currency' => $order->get_currency() ) ); if ( (float) $tax > 0 && $order->get_prices_include_tax() ) { /** * Hook to add tax label to pickup cost. * * @since 6.0.0 * @param string $tax_label Tax label. * @param \WC_Order $order Order object. * @param string $tax_display Tax display. * @return string */ $formatted_cost .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>', $order, $tax_display ); } } else { // Show pickup cost including tax. $formatted_cost = wc_price( (float) $cost + (float) $tax, array( 'currency' => $order->get_currency() ) ); if ( (float) $tax > 0 && ! $order->get_prices_include_tax() ) { /** * Hook to add tax label to pickup cost. * * @since 6.0.0 * @param string $tax_label Tax label. * @param \WC_Order $order Order object. * @param string $tax_display Tax display. * @return string */ $formatted_cost .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>', $order, $tax_display ); } } $lines[] = '<br>' . sprintf( // Translators: %s is the formatted price. __( 'Pickup cost: %s', 'woocommerce' ), $formatted_cost ); } // If nothing is available, return original. if ( empty( $lines ) ) { return $return_value; } // Join all the lines with a <br> separator. return implode( '<br>', $lines ); } /** * When using the cart and checkout blocks this method is used to adjust core shipping settings via a filter hook. * * @param array $settings The default WC shipping settings. * @return array|mixed The filtered settings. */ public function remove_shipping_settings( $settings ) { if ( CartCheckoutUtils::is_cart_block_default() ) { foreach ( $settings as $index => $setting ) { if ( 'woocommerce_enable_shipping_calc' === $setting['id'] ) { $settings[ $index ]['desc_tip'] = sprintf( /* translators: %s: URL to the documentation. */ __( 'This feature is not available when using the <a href="%s">Cart and checkout blocks</a>. Shipping will be calculated at checkout.', 'woocommerce' ), 'https://woocommerce.com/document/woocommerce-store-editing/customizing-cart-and-checkout/' ); $settings[ $index ]['disabled'] = true; $settings[ $index ]['value'] = 'no'; break; } } } return $settings; } /** * Register Local Pickup settings for rest api. */ public function register_settings() { register_setting( 'options', 'woocommerce_pickup_location_settings', array( 'type' => 'object', 'description' => 'WooCommerce Local Pickup Method Settings', 'default' => array(), 'show_in_rest' => array( 'name' => 'pickup_location_settings', 'schema' => array( 'type' => 'object', 'properties' => array( 'enabled' => array( 'description' => __( 'If enabled, this method will appear on the block based checkout.', 'woocommerce' ), 'type' => 'string', 'enum' => array( 'yes', 'no' ), ), 'title' => array( 'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ), 'type' => 'string', ), 'tax_status' => array( 'description' => __( 'If a cost is defined, this controls if taxes are applied to that cost.', 'woocommerce' ), 'type' => 'string', 'enum' => array( ProductTaxStatus::TAXABLE, ProductTaxStatus::NONE ), ), 'cost' => array( 'description' => __( 'Optional cost to charge for local pickup.', 'woocommerce' ), 'type' => 'string', ), ), ), ), ) ); register_setting( 'options', 'pickup_location_pickup_locations', array( 'type' => 'array', 'description' => 'WooCommerce Local Pickup Locations', 'default' => array(), 'show_in_rest' => array( 'name' => 'pickup_locations', 'schema' => array( 'type' => 'array', 'items' => array( 'type' => 'object', 'properties' => array( 'name' => array( 'type' => 'string', ), 'address' => array( 'type' => 'object', 'properties' => array( 'address_1' => array( 'type' => 'string', ), 'city' => array( 'type' => 'string', ), 'state' => array( 'type' => 'string', ), 'postcode' => array( 'type' => 'string', ), 'country' => array( 'type' => 'string', ), ), ), 'details' => array( 'type' => 'string', ), 'enabled' => array( 'type' => 'boolean', ), ), ), ), ), ) ); } /** * Hydrate client settings */ public function hydrate_client_settings() { if ( ! wp_script_is( self::LOCAL_PICKUP_ADMIN_JS_HANDLE, 'enqueued' ) ) { // Only hydrate the settings if the script dependent on them is enqueued. return; } $locations = get_option( 'pickup_location_pickup_locations', array() ); $formatted_pickup_locations = array(); foreach ( $locations as $location ) { $formatted_pickup_locations[] = array( 'name' => $location['name'], 'address' => $location['address'], 'details' => $location['details'], 'enabled' => wc_string_to_bool( $location['enabled'] ), ); } $has_legacy_pickup = false; // Get all shipping zones. $shipping_zones = \WC_Shipping_Zones::get_zones( 'admin' ); $international_shipping_zone = new \WC_Shipping_Zone( 0 ); // Loop through each shipping zone. foreach ( $shipping_zones as $shipping_zone ) { // Get all registered rates for this shipping zone. $shipping_methods = $shipping_zone['shipping_methods']; // Loop through each registered rate. foreach ( $shipping_methods as $shipping_method ) { if ( 'local_pickup' === $shipping_method->id && 'yes' === $shipping_method->enabled ) { $has_legacy_pickup = true; break 2; } } } foreach ( $international_shipping_zone->get_shipping_methods( true ) as $shipping_method ) { if ( 'local_pickup' === $shipping_method->id ) { $has_legacy_pickup = true; break; } } $settings = array( 'pickupLocationSettings' => LocalPickupUtils::get_local_pickup_settings(), 'pickupLocations' => $formatted_pickup_locations, 'readonlySettings' => array( 'hasLegacyPickup' => $has_legacy_pickup, 'storeCountry' => WC()->countries->get_base_country(), 'storeState' => WC()->countries->get_base_state(), ), ); wp_add_inline_script( self::LOCAL_PICKUP_ADMIN_JS_HANDLE, sprintf( 'var hydratedScreenSettings = %s;', wp_json_encode( $settings, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) ), 'before' ); } /** * Load admin scripts. */ public function admin_scripts() { $this->asset_api->register_script( self::LOCAL_PICKUP_ADMIN_JS_HANDLE, 'assets/client/blocks/wc-shipping-method-pickup-location.js', array(), true ); } /** * Registers the Local Pickup shipping method used by the Checkout Block. */ public function register_local_pickup() { if ( CartCheckoutUtils::is_checkout_block_default() ) { $wc_instance = WC(); if ( is_object( $wc_instance ) && method_exists( $wc_instance, 'shipping' ) && is_object( $wc_instance->shipping ) && method_exists( $wc_instance->shipping, 'register_shipping_method' ) ) { $wc_instance->shipping->register_shipping_method( new PickupLocation() ); } else { wc_get_logger()->error( 'Error registering pickup location: WC()->shipping->register_shipping_method is not available', array( 'source' => 'shipping-controller' ) ); } } } /** * Declares the Pickup Location shipping method as a Local Pickup method for WooCommerce. * * @param array $methods Shipping method ids. * @return array */ public function register_local_pickup_method( $methods ) { $methods[] = 'pickup_location'; return $methods; } /** * Hides the shipping address on the order confirmation page when local pickup is selected. * * @param array $pickup_methods Method ids. * @return array */ public function hide_shipping_address_for_local_pickup( $pickup_methods ) { return array_merge( $pickup_methods, LocalPickupUtils::get_local_pickup_method_ids() ); } /** * Everytime we save or update local pickup settings, we flush the shipping * transient group. * * @param array $settings The setting array we're saving. * @return array $settings The setting array we're saving. */ public function flush_cache( $settings ) { \WC_Cache_Helper::get_transient_version( 'shipping', true ); return $settings; } /** * Filter the location used for taxes based on the chosen pickup location. * * @param array $address Location args. * @return array */ public function filter_taxable_address( $address ) { if ( null === WC()->session ) { return $address; } // We only need to select from the first package, since pickup_location only supports a single package. $chosen_method = current( WC()->session->get( 'chosen_shipping_methods', array() ) ) ?? ''; $chosen_method_id = explode( ':', $chosen_method )[0]; $chosen_method_instance = explode( ':', $chosen_method )[1] ?? 0; // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment if ( $chosen_method_id && true === apply_filters( 'woocommerce_apply_base_tax_for_local_pickup', true ) && in_array( $chosen_method_id, LocalPickupUtils::get_local_pickup_method_ids(), true ) ) { $pickup_locations = get_option( 'pickup_location_pickup_locations', array() ); $pickup_location = $pickup_locations[ $chosen_method_instance ] ?? array(); if ( isset( $pickup_location['address'], $pickup_location['address']['country'] ) && ! empty( $pickup_location['address']['country'] ) ) { $address = array( $pickup_locations[ $chosen_method_instance ]['address']['country'], $pickup_locations[ $chosen_method_instance ]['address']['state'], $pickup_locations[ $chosen_method_instance ]['address']['postcode'], $pickup_locations[ $chosen_method_instance ]['address']['city'], ); } } return $address; } /** * Local Pickup requires all packages to support local pickup. This is because the entire order must be picked up * so that all packages get the same tax rates applied during checkout. * * If a shipping package does not support local pickup (e.g. if disabled by an extension), this filters the option * out for all packages. This will in turn disable the "pickup" toggle in Block Checkout. * * @param array $packages Array of shipping packages. * @return array */ public function filter_shipping_packages( $packages ) { // Check all packages for an instance of a collectable shipping method. $valid_packages = array_filter( $packages, function ( $package ) { $shipping_method_ids = ArrayUtil::select( $package['rates'] ?? array(), 'get_method_id', ArrayUtil::SELECT_BY_OBJECT_METHOD ); return ! empty( array_intersect( LocalPickupUtils::get_local_pickup_method_ids(), $shipping_method_ids ) ); } ); // Remove pickup location from rates arrays if not all packages can be picked up or support local pickup. if ( count( $valid_packages ) !== count( $packages ) ) { $packages = array_map( function ( $package ) { if ( ! is_array( $package['rates'] ) ) { $package['rates'] = array(); return $package; } $package['rates'] = array_filter( $package['rates'], function ( $rate ) { return ! in_array( $rate->get_method_id(), LocalPickupUtils::get_local_pickup_method_ids(), true ); } ); return $package; }, $packages ); } return $packages; } /** * Remove shipping (i.e. delivery, not local pickup) if "Hide shipping costs until an address is entered" is enabled, * and no address has been entered yet. * * Only applies to block checkout because pickup is chosen separately to shipping in that context. * * @param array $packages Array of shipping packages. * @return array */ public function remove_shipping_if_no_address( $packages ) { if ( 'shortcode' === WC()->cart->cart_context ) { return $packages; } $shipping_cost_requires_address = wc_string_to_bool( get_option( 'woocommerce_shipping_cost_requires_address', 'no' ) ); // Return early here for a small performance gain if we don't need to hide shipping costs until an address is entered. if ( ! $shipping_cost_requires_address ) { return $packages; } $customer = WC()->customer; if ( $customer instanceof WC_Customer && $customer->has_full_shipping_address() ) { return $packages; } return array_map( function ( $package ) { // Package rates is always an array due to a check in core. $package['rates'] = array_filter( $package['rates'], function ( $rate ) { return $rate instanceof WC_Shipping_Rate && in_array( $rate->get_method_id(), LocalPickupUtils::get_local_pickup_method_ids(), true ); } ); return $package; }, $packages ); } /** * Track local pickup settings changes via Store API * * @param bool $served Whether the request has already been served. * @param \WP_REST_Response $result The response object. * @param \WP_REST_Request $request The request object. * @return bool */ public function track_local_pickup( $served, $result, $request ) { if ( '/wp/v2/settings' !== $request->get_route() ) { return $served; } // Param name here comes from the show_in_rest['name'] value when registering the setting. if ( ! $request->get_param( 'pickup_location_settings' ) && ! $request->get_param( 'pickup_locations' ) ) { return $served; } $event_name = 'local_pickup_save_changes'; $settings = $request->get_param( 'pickup_location_settings' ); $locations = $request->get_param( 'pickup_locations' ); $data = array( 'local_pickup_enabled' => 'yes' === $settings['enabled'] ? true : false, 'title' => __( 'Pickup', 'woocommerce' ) === $settings['title'], 'price' => '' === $settings['cost'] ? true : false, 'cost' => '' === $settings['cost'] ? 0 : $settings['cost'], 'taxes' => $settings['tax_status'], 'total_pickup_locations' => count( $locations ), 'pickup_locations_enabled' => count( array_filter( $locations, function ( $location ) { return $location['enabled']; } ) ), ); WC_Tracks::record_event( $event_name, $data ); return $served; } /** * Check if legacy local pickup is activated in any of the shipping zones or in the Rest of the World zone. * * @since 8.8.0 * * @return bool */ public static function is_legacy_local_pickup_active() { $rest_of_the_world = \WC_Shipping_Zones::get_zone_by( 'zone_id', 0 ); $shipping_zones = \WC_Shipping_Zones::get_zones(); $rest_of_the_world_data = $rest_of_the_world->get_data(); $rest_of_the_world_data['shipping_methods'] = $rest_of_the_world->get_shipping_methods(); array_unshift( $shipping_zones, $rest_of_the_world_data ); foreach ( $shipping_zones as $zone ) { foreach ( $zone['shipping_methods'] as $method ) { if ( 'local_pickup' === $method->id && $method->is_enabled() ) { return true; } } } return false; } } PK SO.]��LV V Shipping/PickupLocation.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\Shipping; use WC_Shipping_Method; /** * Local Pickup Shipping Method. */ class PickupLocation extends WC_Shipping_Method { /** * Pickup locations. * * @var array */ protected $pickup_locations = []; /** * Cost * * @var string */ protected $cost = ''; /** * Constructor. */ public function __construct() { parent::__construct(); $this->id = 'pickup_location'; $this->method_title = __( 'Local pickup', 'woocommerce' ); $this->method_description = __( 'Allow customers to choose a local pickup location during checkout.', 'woocommerce' ); $this->init(); } /** * Init function. */ public function init() { $this->enabled = $this->get_option( 'enabled' ); $this->title = $this->get_option( 'title', __( 'Pickup', 'woocommerce' ) ); $this->tax_status = $this->get_option( 'tax_status' ); $this->cost = $this->get_option( 'cost' ); $this->supports = [ 'settings', 'local-pickup' ]; $this->pickup_locations = get_option( $this->id . '_pickup_locations', [] ); add_filter( 'woocommerce_attribute_label', array( $this, 'translate_meta_data' ), 10, 3 ); } /** * Checks if a given address is complete. * * @param array $address Address. * @return bool */ protected function has_valid_pickup_location( $address ) { // Normalize address. $address_fields = wp_parse_args( (array) $address, array( 'city' => '', 'postcode' => '', 'state' => '', 'country' => '', ) ); // Country is always required. if ( empty( $address_fields['country'] ) ) { return false; } // If all fields are provided, we can skip further checks. if ( ! empty( $address_fields['city'] ) && ! empty( $address_fields['postcode'] ) && ! empty( $address_fields['state'] ) ) { return true; } // Check validity based on requirements for the country. $country_address_fields = wc()->countries->get_address_fields( $address_fields['country'], 'shipping_' ); foreach ( $country_address_fields as $field_name => $field ) { $key = str_replace( 'shipping_', '', $field_name ); if ( isset( $address_fields[ $key ] ) && true === $field['required'] && empty( $address_fields[ $key ] ) ) { return false; } } return true; } /** * Calculate shipping. * * @param array $package Package information. */ public function calculate_shipping( $package = array() ) { if ( $this->pickup_locations ) { foreach ( $this->pickup_locations as $index => $location ) { if ( ! $location['enabled'] ) { continue; } $this->add_rate( array( 'id' => $this->id . ':' . $index, // This is the label shown in shipping rate/method context e.g. London (Local Pickup). 'label' => wp_kses_post( $this->title . ' (' . $location['name'] . ')' ), 'package' => $package, 'cost' => $this->cost, 'meta_data' => array( 'pickup_location' => wp_kses_post( $location['name'] ), 'pickup_address' => $this->has_valid_pickup_location( $location['address'] ) ? wc()->countries->get_formatted_address( $location['address'], ', ' ) : '', 'pickup_details' => wp_kses_post( $location['details'] ), ), ) ); } } } /** * See if the method is available. * * @param array $package Package information. * @return bool */ public function is_available( $package ) { // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment return apply_filters( 'woocommerce_shipping_' . $this->id . '_is_available', 'yes' === $this->enabled, $package, $this ); } /** * Translates meta data for the shipping method. * * @param string $label Meta label. * @param string $name Meta key. * @param mixed $product Product if applicable. * @return string */ public function translate_meta_data( $label, $name, $product ) { if ( $product ) { return $label; } switch ( $name ) { case 'pickup_location': return __( 'Pickup location', 'woocommerce' ); case 'pickup_address': return __( 'Pickup address', 'woocommerce' ); case 'pickup_details': return __( 'Pickup details', 'woocommerce' ); } return $label; } /** * Admin options screen. * * See also WC_Shipping_Method::admin_options(). */ public function admin_options() { global $hide_save_button; $hide_save_button = true; wp_enqueue_script( 'wc-shipping-method-pickup-location' ); echo '<h2>' . esc_html__( 'Local pickup', 'woocommerce' ) . '</h2>'; echo '<div class="wrap"><div id="wc-shipping-method-pickup-location-settings-container"></div></div>'; } } PK SO.]|�� Registry/Container.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\Registry; use Closure; use Exception; /** * A simple Dependency Injection Container * * This is used to manage dependencies used throughout the plugin. * * @since 2.5.0 */ class Container { /** * A map of Dependency Type objects used to resolve dependencies. * * @var AbstractDependencyType[] */ private $registry = []; /** * Public api for adding a factory to the container. * * Factory dependencies will have the instantiation callback invoked * every time the dependency is requested. * * Typical Usage: * * ``` * $container->register( MyClass::class, $container->factory( $mycallback ) ); * ``` * * @param Closure $instantiation_callback This will be invoked when the * dependency is required. It will * receive an instance of this * container so the callback can * retrieve dependencies from the * container. * * @return FactoryType An instance of the FactoryType dependency. */ public function factory( Closure $instantiation_callback ) { return new FactoryType( $instantiation_callback ); } /** * Interface for registering a new dependency with the container. * * By default, the $value will be added as a shared dependency. This means * that it will be a single instance shared among any other classes having * that dependency. * * If you want a new instance every time it's required, then wrap the value * in a call to the factory method (@see Container::factory for example) * * Note: Currently if the provided id already is registered in the container, * the provided value is ignored. * * @param string $id A unique string identifier for the provided value. * Typically it's the fully qualified name for the * dependency. * @param mixed $value The value for the dependency. Typically, this is a * closure that will create the class instance needed. */ public function register( $id, $value ) { if ( empty( $this->registry[ $id ] ) ) { if ( ! $value instanceof FactoryType ) { $value = new SharedType( $value ); } $this->registry[ $id ] = $value; } } /** * Interface for retrieving the dependency stored in the container for the * given identifier. * * @param string $id The identifier for the dependency being retrieved. * @throws Exception If there is no dependency for the given identifier in * the container. * * @return mixed Typically a class instance. */ public function get( $id ) { if ( ! isset( $this->registry[ $id ] ) ) { // this is a developer facing exception, hence it is not localized. throw new Exception( sprintf( 'Cannot construct an instance of %s because it has not been registered.', $id ) ); } return $this->registry[ $id ]->get( $this ); } } PK SO.]�Ǖ�� � # Registry/AbstractDependencyType.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\Registry; /** * An abstract class for dependency types. * * Dependency types are instances of a dependency used by the * Dependency Injection Container for storing dependencies to invoke as they * are needed. * * @since 2.5.0 */ abstract class AbstractDependencyType { /** * Holds a callable or value provided for this type. * * @var mixed */ private $callable_or_value; /** * Constructor * * @param mixed $callable_or_value A callable or value for the dependency * type instance. */ public function __construct( $callable_or_value ) { $this->callable_or_value = $callable_or_value; } /** * Resolver for the internal dependency value. * * @param Container $container The Dependency Injection Container. * * @return mixed */ protected function resolve_value( Container $container ) { $callback = $this->callable_or_value; return \is_callable( $callback ) ? $callback( $container ) : $callback; } /** * Retrieves the value stored internally for this DependencyType * * @param Container $container The Dependency Injection Container. * * @return void */ abstract public function get( Container $container ); } PK SO.]��,g� � Registry/FactoryType.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\Registry; /** * Definition for the FactoryType dependency type. * * @since 2.5.0 */ class FactoryType extends AbstractDependencyType { /** * Invokes and returns the value from the stored internal callback. * * @param Container $container An instance of the dependency injection * container. * * @return mixed */ public function get( Container $container ) { return $this->resolve_value( $container ); } } PK SO.]���$� � Registry/SharedType.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\Registry; /** * A definition for the SharedType dependency type. * * @since 2.5.0 */ class SharedType extends AbstractDependencyType { /** * Holds a cached instance of the value stored (or returned) internally. * * @var mixed */ private $shared_instance; /** * Returns the internal stored and shared value after initial generation. * * @param Container $container An instance of the dependency injection * container. * * @return mixed */ public function get( Container $container ) { if ( empty( $this->shared_instance ) ) { $this->shared_instance = $this->resolve_value( $container ); } return $this->shared_instance; } } PK SO.]��J � � ! BlockTypes/ProductFilterChips.phpnu ��� <?php declare( strict_types = 1 ); namespace Automattic\WooCommerce\Blocks\BlockTypes; /** * Product Filter: Chips Block. */ final class ProductFilterChips extends AbstractBlock { use EnableBlockJsonAssetsTrait; /** * Block name. * * @var string */ protected $block_name = 'product-filter-chips'; /** * Render the block. * * @param array $attributes Block attributes. * @param string $content Block content. * @param WP_Block $block Block instance. * @return string Rendered block type output. */ protected function render( $attributes, $content, $block ) { if ( empty( $block->context['filterData'] ) ) { return ''; } $items = $block->context['filterData']['items'] ?? array(); $show_counts = $block->context['filterData']['showCounts'] ?? false; $classes = ''; $style = ''; $tags = new \WP_HTML_Tag_Processor( $content ); if ( $tags->next_tag( array( 'class_name' => 'wc-block-product-filter-chips' ) ) ) { $classes = $tags->get_attribute( 'class' ); $style = $tags->get_attribute( 'style' ); } $checked_items = array_filter( $items, function ( $item ) { return $item['selected']; } ); $show_initially = 15; $remaining_initial_unchecked = count( $checked_items ) > $show_initially ? count( $checked_items ) : $show_initially - count( $checked_items ); $count = 0; $wrapper_attributes = array( 'data-wp-interactive' => 'woocommerce/product-filters', 'data-wp-key' => wp_unique_prefixed_id( $this->get_full_block_name() ), 'data-wp-context' => '{}', 'class' => esc_attr( $classes ), ); if ( ! empty( $style ) ) { // Styles generated by Supports API doesn't include semicolon at the end. $wrapper_attributes['style'] = esc_attr( $style ) . ';'; } ob_start(); ?> <div <?php echo get_block_wrapper_attributes( $wrapper_attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> <fieldset> <?php if ( ! empty( $block->context['filterData']['groupLabel'] ) ) : ?> <legend class="screen-reader-text"><?php echo esc_html( $block->context['filterData']['groupLabel'] ); ?></legend> <?php endif; ?> <div class="wc-block-product-filter-chips__items"> <?php foreach ( $items as $item ) { ?> <?php $item_id = $item['type'] . '-' . $item['value']; ?> <button data-wp-key="<?php echo esc_attr( $item_id ); ?>" id="<?php echo esc_attr( $item_id ); ?>" class="wc-block-product-filter-chips__item" type="button" role="checkbox" aria-label="<?php echo esc_attr( $this->get_aria_label( $item, $show_counts ) ); ?>" data-wp-on--click="actions.toggleFilter" value="<?php echo esc_attr( $item['value'] ); ?>" data-wp-bind--aria-checked="state.isFilterSelected" <?php echo wp_interactivity_data_wp_context( array( 'item' => $item ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> <?php if ( ! $item['selected'] ) : ?> <?php if ( $count >= $remaining_initial_unchecked ) : ?> data-wp-bind--hidden="!context.showAll" hidden <?php else : ?> <?php ++$count; ?> <?php endif; ?> <?php endif; ?> > <span class="wc-block-product-filter-chips__label"> <span class="wc-block-product-filter-chips__text"> <?php echo $item['label']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> </span> <?php if ( $show_counts ) : ?> <span class="wc-block-product-filter-chips__count"> (<?php echo esc_html( $item['count'] ); ?>) </span> <?php endif; ?> </span> </button> <?php } ?> </div> <?php if ( count( $items ) > $show_initially ) : ?> <button class="wc-block-product-filter-chips__show-more" data-wp-on--click="actions.showAllChips" data-wp-bind--hidden="context.showAll" hidden > <?php echo esc_html__( 'Show more…', 'woocommerce' ); ?> </button> <?php endif; ?> </fieldset> </div> <?php return ob_get_clean(); } /** * Get aria label for filter item. * * @param array $item Filter item. * @param bool $show_counts Whether to show counts. * * @return string Aria label. */ private function get_aria_label( $item, $show_counts ) { if ( $show_counts ) { return sprintf( /* translators: %1$s: Product filter name, %2$d: Number of products */ _n( '%1$s (%2$d product)', '%1$s (%2$d products)', $item['count'], 'woocommerce' ), $item['ariaLabel'] ?? $item['label'], $item['count'] ); } return $item['ariaLabel'] ?? $item['label']; } } PK SO.]�o~G + BlockTypes/CheckoutShippingAddressBlock.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\BlockTypes; /** * CheckoutShippingAddressBlock class. */ class CheckoutShippingAddressBlock extends AbstractInnerBlock { /** * Block name. * * @var string */ protected $block_name = 'checkout-shipping-address-block'; } PK SO.]H��� . BlockTypes/CheckoutContactInformationBlock.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\BlockTypes; /** * CheckoutContactInformationBlock class. */ class CheckoutContactInformationBlock extends AbstractInnerBlock { /** * Block name. * * @var string */ protected $block_name = 'checkout-contact-information-block'; } PK SO.]�k��` ` BlockTypes/ProductRating.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\BlockTypes; use Automattic\WooCommerce\Blocks\Utils\StyleAttributesUtils; /** * ProductRating class. */ class ProductRating extends AbstractBlock { /** * Block name. * * @var string */ protected $block_name = 'product-rating'; /** * API version name. * * @var string */ protected $api_version = '3'; /** * Get the block's attributes. * * @param array $attributes Block attributes. Default empty array. * @return array Block attributes merged with defaults. */ private function parse_attributes( $attributes ) { // These should match what's set in JS `registerBlockType`. $defaults = array( 'productId' => 0, 'isDescendentOfQueryLoop' => false, 'textAlign' => '', 'isDescendentOfSingleProductBlock' => false, 'isDescendentOfSingleProductTemplate' => false, ); return wp_parse_args( $attributes, $defaults ); } /** * Overwrite parent method to prevent script registration. * * It is necessary to register and enqueues assets during the render * phase because we want to load assets only if the block has the content. */ protected function register_block_type_assets() { return null; } /** * Get the frontend style handle for this block type. * * @return null */ protected function get_block_type_style() { return array_merge( parent::get_block_type_style(), [ 'wc-blocks-packages-style' ] ); } /** * Register the context. */ protected function get_block_type_uses_context() { return [ 'query', 'queryId', 'postId' ]; } /** * Include and render the block. * * @param array $attributes Block attributes. Default empty array. * @param string $content Block content. Default empty string. * @param WP_Block $block Block instance. * @return string Rendered block type output. */ protected function render( $attributes, $content, $block ) { if ( ! empty( $content ) ) { parent::register_block_type_assets(); $this->register_chunk_translations( [ $this->block_name ] ); return $content; } $post_id = isset( $block->context['postId'] ) ? $block->context['postId'] : ''; $product = wc_get_product( $post_id ); if ( $product && $product->get_review_count() > 0 && $product->get_reviews_allowed() && wc_reviews_enabled() ) { $product_reviews_count = $product->get_review_count(); $product_rating = $product->get_average_rating(); $parsed_attributes = $this->parse_attributes( $attributes ); $is_descendent_of_single_product_block = $parsed_attributes['isDescendentOfSingleProductBlock']; $is_descendent_of_single_product_template = $parsed_attributes['isDescendentOfSingleProductTemplate']; $styles_and_classes = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); $text_align_styles_and_classes = StyleAttributesUtils::get_text_align_class_and_style( $attributes ); /** * Filter the output from wc_get_rating_html. * * @param string $html Star rating markup. Default empty string. * @param float $rating Rating being shown. * @param int $count Total number of ratings. * @return string */ $filter_rating_html = function( $html, $rating, $count ) use ( $post_id, $product_rating, $product_reviews_count, $is_descendent_of_single_product_block, $is_descendent_of_single_product_template ) { $product_permalink = get_permalink( $post_id ); $reviews_count = $count; $average_rating = $rating; if ( $product_rating ) { $average_rating = $product_rating; } if ( $product_reviews_count ) { $reviews_count = $product_reviews_count; } if ( 0 < $average_rating || false === $product_permalink ) { /* translators: %s: rating */ $label = sprintf( __( 'Rated %s out of 5', 'woocommerce' ), $average_rating ); $customer_reviews_count = sprintf( /* translators: %s is referring to the total of reviews for a product */ _n( '(%s customer review)', '(%s customer reviews)', $reviews_count, 'woocommerce' ), esc_html( $reviews_count ) ); if ( $is_descendent_of_single_product_block ) { $customer_reviews_count = '<a href="' . esc_url( $product_permalink ) . '#reviews">' . $customer_reviews_count . '</a>'; } elseif ( $is_descendent_of_single_product_template ) { $customer_reviews_count = '<a class="woocommerce-review-link" rel="nofollow" href="#reviews">' . $customer_reviews_count . '</a>'; } $reviews_count_html = sprintf( '<span class="wc-block-components-product-rating__reviews_count">%1$s</span>', $customer_reviews_count ); $html = sprintf( '<div class="wc-block-components-product-rating__container"> <div class="wc-block-components-product-rating__stars wc-block-grid__product-rating__stars" role="img" aria-label="%1$s"> %2$s </div> %3$s </div> ', esc_attr( $label ), wc_get_star_rating_html( $average_rating, $reviews_count ), $is_descendent_of_single_product_block || $is_descendent_of_single_product_template ? $reviews_count_html : '' ); } else { $html = ''; } return $html; }; add_filter( 'woocommerce_product_get_rating_html', $filter_rating_html, 10, 3 ); $rating_html = wc_get_rating_html( $product->get_average_rating() ); remove_filter( 'woocommerce_product_get_rating_html', $filter_rating_html, 10 ); $classes = implode( ' ', array_filter( array( 'wc-block-components-product-rating wc-block-grid__product-rating', esc_attr( $text_align_styles_and_classes['class'] ?? '' ), esc_attr( $styles_and_classes['classes'] ), ) ) ); $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classes, 'style' => esc_attr( $styles_and_classes['styles'] ?? '' ), ) ); return sprintf( '<div %1$s> %2$s </div>', $wrapper_attributes, $rating_html ); } return ''; } } PK SO.]��� - BlockTypes/MiniCartTitleItemsCounterBlock.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\BlockTypes; use Automattic\WooCommerce\Admin\Features\Features; /** * MiniCartTitleItemsCounterBlock class. */ class MiniCartTitleItemsCounterBlock extends AbstractInnerBlock { /** * Block name. * * @var string */ protected $block_name = 'mini-cart-title-items-counter-block'; /** * Render the block. * * @param array $attributes Block attributes. * @param string $content Block content. * @param WP_Block $block Block instance. * @return string Rendered block type output. */ protected function render( $attributes, $content, $block ) { if ( Features::is_enabled( 'experimental-iapi-mini-cart' ) ) { return $this->render_experimental_iapi_title_label_block(); } return $content; } /** * Render the interactivity API powered experimental title block. * * @return string Rendered block type output. */ protected function render_experimental_iapi_title_label_block() { $cart = $this->get_cart_instance(); $cart_item_count = $cart ? $cart->get_cart_contents_count() : 0; // The following translation is a temporary workaround. It will be // reverted to the previous form `(%d items)` as soon as the // `@wordpress/i18n` package is available as a script module. // translators: %d number of items in the cart. $cart_item_text = __( '(items: %d)', 'woocommerce' ); wp_interactivity_config( $this->get_full_block_name(), array( 'itemsInCartTextTemplate' => $cart_item_text, ) ); wp_interactivity_state( $this->get_full_block_name(), array( 'itemsInCartText' => sprintf( $cart_item_text, $cart_item_count ), ) ); $wrapper_attributes = get_block_wrapper_attributes( array( 'data-wp-text' => 'state.itemsInCartText', 'data-wp-interactive' => 'woocommerce/mini-cart-title-items-counter-block', ) ); ob_start(); ?> <span <?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> </span> <?php return ob_get_clean(); } /** * Return the main instance of WC_Cart class. * * @return \WC_Cart CartController class instance. */ protected function get_cart_instance() { $cart = WC()->cart; if ( $cart && $cart instanceof \WC_Cart ) { return $cart; } return null; } } PK SO.]�� � BlockTypes/AtomicBlock.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\BlockTypes; /** * AtomicBlock class. * * @internal */ class AtomicBlock extends AbstractBlock { /** * Get the editor script data for this block type. * * @param string $key Data to get, or default to everything. * @return null */ protected function get_block_type_editor_script( $key = null ) { return null; } /** * Get the editor style handle for this block type. * * @return null */ protected function get_block_type_editor_style() { return null; } /** * Get the frontend script handle for this block type. * * @param string $key Data to get, or default to everything. * @return null */ protected function get_block_type_script( $key = null ) { return null; } /** * Get the frontend style handle for this block type. * * @return null */ protected function get_block_type_style() { return null; } } PK SO.]% �5� � $ BlockTypes/ProductSpecifications.phpnu ��� <?php declare(strict_types=1); namespace Automattic\WooCommerce\Blocks\BlockTypes; use Automattic\WooCommerce\Enums\ProductType; /** * ProductSpecifications class. */ class ProductSpecifications extends AbstractBlock { /** * Block name. * * @var string */ protected $block_name = 'product-specifications'; /** * Get the frontend script handle for this block type. * * @param string $key Data to get, or default to everything. */ protected function get_block_type_script( $key = null ) { return null; } /** * Render the block. * * @param array $attributes Block attributes. * @param string $content Block content. * @param WP_Block $block Block instance. * * @return string Rendered block output. */ protected function render( $attributes, $content, $block ) { if ( ! isset( $block->context['postId'] ) ) { return ''; } $product = wc_get_product( $block->context['postId'] ); if ( ! $product ) { return ''; } $product_data = array(); // Get display settings with defaults. $show_weight = isset( $attributes['showWeight'] ) ? $attributes['showWeight'] : true; $show_dimensions = isset( $attributes['showDimensions'] ) ? $attributes['showDimensions'] : true; $show_attributes = isset( $attributes['showAttributes'] ) ? $attributes['showAttributes'] : true; if ( $show_weight && $product->has_weight() ) { $product_data['weight'] = array( 'label' => __( 'Weight', 'woocommerce' ), 'value' => wc_format_weight( $product->get_weight() ), ); } if ( $show_dimensions && $product->has_dimensions() ) { $product_data['dimensions'] = array( 'label' => __( 'Dimensions', 'woocommerce' ), 'value' => wc_format_dimensions( $product->get_dimensions( false ) ), ); } $is_interactive = $product->is_type( ProductType::VARIABLE ); if ( $is_interactive ) { $variations = $product->get_available_variations( 'objects' ); $formatted_variations_data = array(); foreach ( $variations as $variation ) { $formatted_variations_data[ $variation->get_id() ] = array( 'weight' => wc_format_weight( $variation->get_weight() ), 'dimensions' => html_entity_decode( wc_format_dimensions( $variation->get_dimensions( false ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ), ); } wp_interactivity_config( 'woocommerce', array( 'products' => array( $product->get_id() => array( 'weight' => $product_data['weight']['value'] ?? '', 'dimensions' => html_entity_decode( $product_data['dimensions']['value'] ?? '', ENT_QUOTES, get_bloginfo( 'charset' ) ), 'variations' => $formatted_variations_data, ), ), ) ); wp_enqueue_script_module( 'woocommerce/product-elements' ); } if ( $show_attributes ) { foreach ( $product->get_attributes() as $attribute ) { $values = array(); if ( $attribute->is_taxonomy() ) { $attribute_taxonomy = $attribute->get_taxonomy_object(); $attribute_values = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) ); foreach ( $attribute_values as $attribute_value ) { $value_name = esc_html( $attribute_value->name ); if ( $attribute_taxonomy->attribute_public ) { $values[] = '<a href="' . esc_url( get_term_link( $attribute_value->term_id, $attribute->get_name() ) ) . '" rel="tag">' . $value_name . '</a>'; } else { $values[] = $value_name; } } } else { $values = $attribute->get_options(); foreach ( $values as &$value ) { $value = make_clickable( esc_html( $value ) ); } } $product_data[ 'attribute_' . sanitize_title_with_dashes( $attribute->get_name() ) ] = array( 'label' => wc_attribute_label( $attribute->get_name() ), 'value' => wpautop( wptexturize( implode( ', ', $values ) ) ), ); } } if ( empty( $product_data ) ) { return ''; } ob_start(); $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => 'wp-block-table' ) ); ?> <figure <?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> <table> <thead class="screen-reader-text"> <tr> <th><?php esc_html_e( 'Attributes', 'woocommerce' ); ?></th> <th><?php esc_html_e( 'Value', 'woocommerce' ); ?></th> </tr> </thead> <tbody> <?php foreach ( $product_data as $product_attribute_key => $product_attribute ) : ?> <tr class="wp-block-product-specifications-item wp-block-product-specifications-item-<?php echo esc_attr( $product_attribute_key ); ?>"> <th scope="row" class="wp-block-product-specifications-item__label"> <?php echo wp_kses_post( $product_attribute['label'] ); ?> </th> <?php if ( $is_interactive && in_array( $product_attribute_key, array( 'weight', 'dimensions' ), true ) ) : ?> <td class="wp-block-product-specifications-item__value" data-wp-interactive="woocommerce/product-elements" data-wp-text="state.productData.<?php echo esc_attr( $product_attribute_key ); ?>"> <?php echo wp_kses_post( $product_attribute['value'] ); ?> </td> <?php else : ?> <td class="wp-block-product-specifications-item__value"> <?php echo wp_kses_post( $product_attribute['value'] ); ?> </td> <?php endif; ?> </tr> <?php endforeach; ?> </tbody> </table> </figure> <?php return ob_get_clean(); } /** * Get the frontend style handle for this block type. * * @return string[] */ protected function get_block_type_style() { $deps = parent::get_block_type_style(); if ( ! is_array( $deps ) ) { return array( 'wp-block-table' ); } return array_merge( array( 'wp-block-table' ), $deps ); } } PK SO.]�Wd ) BlockTypes/CheckoutPickupOptionsBlock.phpnu ��� <?php namespace Automattic\WooCommerce\Blocks\BlockTypes; /** * CheckoutPickupOptionsBlock class. */ class CheckoutPickupOptionsBlock extends AbstractInnerBlock { /** * Block name. * * @var string */ protected $block_name = 'checkout-pickup-options-block'; } PK SO.]r�S3^>