Файловый менеджер - Редактировать - /home/bean7936/perfect-community.com/wp-content/plugins/facebook-for-woocommerce/includes/CollectionPage.php
Назад
<?php /** * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved * * This source code is licensed under the license found in the * LICENSE file in the root directory of this source tree. * * @package MetaCommerce */ namespace WooCommerce\Facebook; use WooCommerce\Facebook\Framework\Logger; defined( 'ABSPATH' ) || exit; /** * Facebook Commerce Recommendation Override for /fbcollection/ * New URL Example: /fbcollection/?clicked_product_id=SKU123_123&shown_product_ids=SKU456_456,SKU789_789 */ class CollectionPage { /** @var string the website url suffix for the collection page */ const ENDPOINT_PATH = '/fbcollection/'; /** @var string the field that should be used for sync'ing the collection page endpoint */ const PRODUCT_FEED_FIELD = 'custom_label_4'; /** @var string Option name for tracking Meta log count */ const META_LOG_COUNTER_OPTION = 'wc_facebook_fbcollection_meta_log_counter'; /** @var int Maximum number of logs to send to Meta per plugin version (for beta testing) */ const META_LOG_MAX_COUNT = 20; public function __construct() { add_action( 'init', [ $this, 'register_rewrite_rule' ] ); add_filter( 'query_vars', [ $this, 'add_query_vars' ] ); add_action( 'woocommerce_product_query', [ $this, 'modify_product_query' ] ); add_filter( 'woocommerce_loop_display_mode', [ $this, 'force_products_display_mode' ], PHP_INT_MAX ); } /** * Register /fbcollection/ as a virtual WooCommerce archive page. */ public function register_rewrite_rule() { add_rewrite_rule( '^fbcollection/?$', 'index.php?post_type=product&custom_fbcollection_page=1', 'top' ); } /** * Add custom query variable. * * @param array $vars Query variables. * @return array */ public function add_query_vars( $vars ) { $vars[] = 'custom_fbcollection_page'; return $vars; } /** * Force "products" display mode on the fbcollection page. * Prevents the WooCommerce "Shop page display" setting from showing * product categories instead of the intended product list. * * @param string $display_mode The current display mode. * @return string */ public function force_products_display_mode( $display_mode ) { if ( 1 === intval( get_query_var( 'custom_fbcollection_page' ) ) ) { return 'products'; } return $display_mode; } /** * Modify WooCommerce product query to inject custom product IDs. * * @param WP_Query $query The WooCommerce product query. */ public function modify_product_query( $query ) { if ( 1 !== intval( get_query_var( 'custom_fbcollection_page' ) ) ) { return; } $final_product_ids = []; // Parse clicked_product_id (urldecode handles double-encoded URLs from Facebook) $clicked_product_id_raw = $this->get_url_params( 'clicked_product_id' ); $clicked_product_id = $this->extract_woo_id_from_retailer_id( $clicked_product_id_raw ); if ( $clicked_product_id ) { $product_id = $this->get_product_id_handle_variations( $clicked_product_id ); if ( false !== $product_id ) { $clicked_product_id = $product_id; $final_product_ids[] = $clicked_product_id; } } // Parse shown_product_ids (urldecode handles double-encoded URLs from Facebook) $shown_product_ids_raw = explode( ',', $this->get_url_params( 'shown_product_ids' ) ); $shown_product_ids = array_map( array( $this, 'extract_woo_id_from_retailer_id' ), array_map( 'sanitize_text_field', $shown_product_ids_raw ) ); $shown_product_ids = array_unique( array_filter( $shown_product_ids ) ); // Remove empty/invalid $shown_product_ids = array_slice( $shown_product_ids, 0, 30 ); // Limit to 30 $shown_product_ids = array_filter( array_filter( array_unique( array_map( array( $this, 'get_product_id_handle_variations' ), $shown_product_ids ) ) ), fn( $id ) => $clicked_product_id !== $id ); $final_product_ids = array_merge( $final_product_ids, $shown_product_ids ); if ( ! empty( $final_product_ids ) ) { $query->set( 'post__in', $final_product_ids ); $query->set( 'orderby', 'post__in' ); $query->set( 'posts_per_page', count( $final_product_ids ) ); // Prevent WooCommerce core and themes from overriding the product order. // Removes itself after first invocation to avoid affecting other queries in the same request. $ordering_override = function ( $_args ) use ( &$ordering_override ) { remove_filter( 'woocommerce_get_catalog_ordering_args', $ordering_override, PHP_INT_MAX ); return array( 'orderby' => 'post__in', 'order' => 'ASC', 'meta_key' => '', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key ); }; add_filter( 'woocommerce_get_catalog_ordering_args', $ordering_override, PHP_INT_MAX ); } else { // Log when no valid products found // Only log for the first N occurrences per plugin version (for beta testing) if ( $this->should_log() ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Public read-only endpoint, URL generated by Facebook Logger::log( 'FBCollection: No valid products found for request: ' . wp_json_encode( $_GET ), [], array( 'should_send_log_to_meta' => true, 'should_save_log_in_woocommerce' => true, 'woocommerce_log_level' => \WC_Log_Levels::WARNING, ) ); // phpcs:enable WordPress.Security.NonceVerification.Recommended } $query->set( 'orderby', 'popularity' ); $query->set( 'posts_per_page', 8 ); } } private function get_url_params( $parameter_name ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization happens after urldecode to prevent encoded XSS return sanitize_text_field( urldecode( wp_unslash( $_GET[ $parameter_name ] ?? '' ) ) ); } /** * Check if we should log the issue and increment the counter. * Returns true for the first N occurrences per plugin version, then false. * Counter resets when the plugin is updated. * * @return bool Whether to send this log to Meta. */ private function should_log() { $current_version = defined( '\WooCommerce\Facebook\PLUGIN_VERSION' ) ? \WooCommerce\Facebook\PLUGIN_VERSION : facebook_for_woocommerce()->get_version(); $option = get_option( self::META_LOG_COUNTER_OPTION, array() ); // Reset counter if plugin version changed. if ( ! is_array( $option ) || ( $option['version'] ?? '' ) !== $current_version ) { $option = array( 'version' => $current_version, 'count' => 0, ); } // Check if we've reached the limit. if ( $option['count'] >= self::META_LOG_MAX_COUNT ) { return false; } // Increment and save. ++$option['count']; update_option( self::META_LOG_COUNTER_OPTION, $option, false ); return true; } /** * Translate a product ID for multi-language sites (WPML/Polylang). * * @param int $product_id The original product ID. * @return int The translated product ID for the current language, or original if no translation. */ private function translate_product_id( $product_id ) { if ( ! $product_id ) { return $product_id; } // WPML support - auto-detect post type to handle both products and variations if ( has_filter( 'wpml_object_id' ) ) { $post_type = get_post_type( $product_id ); if ( $post_type ) { $translated_id = apply_filters( 'wpml_object_id', $product_id, $post_type, true ); if ( $translated_id ) { return $translated_id; } } } // Polylang support if ( function_exists( 'pll_get_post' ) ) { $translated_id = pll_get_post( $product_id ); if ( $translated_id ) { return $translated_id; } } return $product_id; } /** * Get the displayable product ID, handling variations and translations. * Converts variations to their parent products since archive pages display parent products only. * Bundle/composite products are returned as-is if they are valid visible products. * * @param int $product_id The product ID (could be variation, bundle, composite, etc.). * @return int|false The displayable product ID, or false if invalid/not visible. */ private function get_product_id_handle_variations( $product_id ) { if ( ! $product_id ) { return false; } // Translate for multi-language support $product_id = $this->translate_product_id( $product_id ); $product = wc_get_product( $product_id ); if ( ! $product ) { return false; } // Handle variations - return parent variable product if ( $product->is_type( 'variation' ) ) { $parent_id = $product->get_parent_id(); if ( $parent_id ) { $parent_id = $this->translate_product_id( $parent_id ); $product = wc_get_product( $parent_id ); if ( $product ) { $product_id = $parent_id; } else { return false; } } else { return false; } } // For all other product types (simple, variable, bundle, composite, grouped, external, etc.) // check visibility and return as-is if valid if ( ! $product->is_visible() ) { return false; } return $product_id; } /** * Extracts the WooCommerce product ID from a Facebook retailer ID. * * @param string $retailer_id The retailer ID (e.g., "GTIN-12345_789_63", "SKU123_456_63", "wc_post_id_63"). * @return int|false The WooCommerce product ID, or false if invalid. */ private function extract_woo_id_from_retailer_id( $retailer_id ) { if ( empty( $retailer_id ) || ! is_string( $retailer_id ) ) { return false; } $retailer_id = trim( $retailer_id ); // If it's already just a number, return it if ( ctype_digit( $retailer_id ) ) { return absint( $retailer_id ); } // Find the last underscore position $last_underscore = strrpos( $retailer_id, '_' ); if ( false === $last_underscore ) { return false; } // Extract everything after the last underscore $woo_id = substr( $retailer_id, $last_underscore + 1 ); // Validate it's a positive integer if ( ! ctype_digit( $woo_id ) || '' === $woo_id ) { return false; } return absint( $woo_id ); } }
| ver. 1.6 |
Github
|
.
| PHP 8.3.30 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка