HEX
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.30
System: Linux iZj6c1151k3ad370bosnmsZ 3.10.0-1160.76.1.el7.x86_64 #1 SMP Wed Aug 10 16:21:17 UTC 2022 x86_64
User: root (0)
PHP: 7.4.30
Disabled: NONE
Upload Files
File: /var/www/html/sparkle/wp-content/plugins/flexible-product-fields/classes/fpf-product.php
<?php

if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

use WPDesk\FPF\Free\Field\TemplateArgs;
use WPDesk\FPF\Free\Field\Type\RadioType;
use WPDesk\FPF\Free\Field\Type\MultiCheckboxType;

class FPF_Product {

	/**
	 * Priority before default
	 */
	const HOOK_BEFORE_DEFAULT = 9;

	/**
	 * Priority after default
	 */
	const HOOK_AFTER_DEFAULT = 20;

	/**
     * Is hook woocommerce_before_add_to_cart_button already fired?
     *
	 * @var bool
	 */
	private $is_woocommerce_before_add_to_cart_button_fired = false;

	/**
	 * @var null|Flexible_Product_Fields
	 */
    private $_plugin = null;

	/**
	 * @var FPF_Product_Fields|null
	 */
    private $_product_fields = null;

	/**
	 * Product price.
	 *
	 * @var FPF_Product_Price|null
	 */
	private $product_price = null;

	/**
	 * FPF_Product constructor.
	 *
	 * @param Flexible_Product_Fields $plugin
	 * @param FPF_Product_Fields $product_fields
	 */
    public function __construct( Flexible_Product_Fields_Plugin $plugin, FPF_Product_Fields $product_fields, FPF_Product_Price $product_price ) {
        $this->_plugin = $plugin;
        $this->_product_fields = $product_fields;
        $this->product_price = $product_price;
        $this->hooks();
    }

	/**
	 *
	 */
    public function hooks() {

        add_action( 'woocommerce_before_add_to_cart_button', array( $this, 'woocommerce_before_add_to_cart_button' ) );
        add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'woocommerce_after_add_to_cart_button' ) );

        add_filter( 'woocommerce_product_supports', array( $this, 'woocommerce_product_supports' ), 10, 3 );

    }

	/**
	 * @param string $error
	 */
    public function add_error( $error ) {
        wc_add_notice( $error, 'error' );
    }

	/**
	 * @param bool $supports
	 * @param string $feature
	 * @param WC_Product $product
	 *
	 * @return bool
	 */
    public function woocommerce_product_supports( $supports, $feature, $product ) {
        if ( 'ajax_add_to_cart' === $feature && $this->product_has_required_field( $product ) ) {
            $supports = false;
        }
        return $supports;
    }

	/**
	 * @param string $type
	 *
	 * @return array
	 */
    public function get_field_type( $type ) {
        $ret = array();
        $field_types = $this->_product_fields->get_field_types();
        foreach ( $field_types as $field_type ) {
            if ( $field_type['value'] == $type ) {
                return $field_type;
            }
        }
        return $ret;
    }

	/**
	 * @param WC_Product $product
	 *
	 * @return bool
	 */
    public function product_has_required_field( $product ) {
        $fields = $this->get_translated_fields_for_product( $product );
        return $fields['has_required'];
    }

	/**
     * Get translated fields for product.
     * Titles and labels will be translated to current language.
     *
	 * @param WC_Product $product
	 * @param bool|string $hook
	 *
	 * @return array
	 */
    public function get_translated_fields_for_product( $product, $hook = false ) {
        return $this->translate_fields_titles_and_labels( $this->get_fields_for_product( $product, $hook ) );
    }

	/**
	 * @param WC_Product $product
	 * @param bool|string $hook
	 *
	 * @return array
	 */
	public function get_fields_for_product( $product, $hook = false ) {
		$cache_key = 'product_' . wpdesk_get_product_id( $product );
		if ( $hook ) {
			$cache_key .= '_h_' . $hook;
		}
		$ret = $this->_product_fields->cache_get( $cache_key );
		if ( $ret === false ) {
			$ret = array(
				'posts' => array(),
				'fields' => array(),
				'display_fields' => array(),
				'has_required' => false
			);
			$fields_posts = array();
			$args = array(
				'post_type' => 'fpf_fields',
				'posts_per_page' => -1,
				'meta_query' => array(
					array(
						'key' => '_assign_to',
						'value' => 'product',
						'compare' => '='
					),
					array(
						'key' => '_product_id',
						'value' => wpdesk_get_product_id( $product ),
						'compare' => '='
					),
				),
			);
			if ( $hook ) {
				$args['meta_query'][] = array(
					'key' => '_section',
					'value' => $hook,
					'compare' => '='
				);
			}
			$posts = get_posts($args);
			foreach ($posts as $post) {
				$ret['posts'][$post->ID] = $post;
			}

			$categories = wp_get_post_terms( wpdesk_get_product_id( $product ), 'product_cat', array( 'fields' => 'ids' ) );
			foreach ( $categories as $category ) {
				$cat_cache_key = 'category_' . $category;
				if ( $hook ) {
					$cat_cache_key .= '_h_' . $hook;
				}
				$posts = $this->_product_fields->cache_get( $cat_cache_key );
				if ( $posts === false ) {
					$args = array(
						'post_type' => 'fpf_fields',
						'posts_per_page' => -1,
						'meta_query' => array(
							array(
								'key' => '_assign_to',
								'value' => 'category',
								'compare' => '='
							),
							array(
								'key' => '_category_id',
								'value' => $category,
								'compare' => 'in'
							),
						),
					);
					if ($hook) {
						$args['meta_query'][] = array(
							'key' => '_section',
							'value' => $hook,
							'compare' => '='
						);
					}
					$posts = get_posts($args);
					$this->_product_fields->cache_set( $cat_cache_key, $posts );
				}
				foreach ($posts as $post) {
					$ret['posts'][$post->ID] = $post;
				}
			}

			$tags = wp_get_post_terms( wpdesk_get_product_id( $product ), 'product_tag', array( 'fields' => 'ids' ) );
			foreach ( $tags as $tag ) {
				$cat_cache_key = 'tag_' . $tag;
				if ( $hook ) {
					$cat_cache_key .= '_h_' . $hook;
				}
				$posts = $this->_product_fields->cache_get( $cat_cache_key );
				if ( $posts === false ) {
					$args = array(
						'post_type' => 'fpf_fields',
						'posts_per_page' => -1,
						'meta_query' => array(
							array(
								'key' => '_assign_to',
								'value' => 'tag',
								'compare' => '='
							),
							array(
								'key' => '_tag_id',
								'value' => $tag,
								'compare' => 'in'
							),
						),
					);
					if ($hook) {
						$args['meta_query'][] = array(
							'key' => '_section',
							'value' => $hook,
							'compare' => '='
						);
					}
					$posts = get_posts($args);
					$this->_product_fields->cache_set( $cat_cache_key, $posts );
				}
				foreach ($posts as $post) {
					$ret['posts'][$post->ID] = $post;
				}
			}

			$cat_cache_key = 'all';
			if ( $hook ) {
				$cat_cache_key .= '_h_' . $hook;
			}
			$posts = $this->_product_fields->cache_get( $cat_cache_key );
			if ( $posts === false ) {
				$args = array(
					'post_type' => 'fpf_fields',
					'posts_per_page' => -1,
					'meta_query' => array(
						array(
							'key' => '_assign_to',
							'value' => 'all',
							'compare' => '='
						),
					),
				);
				if ($hook) {
					$args['meta_query'][] = array(
						'key' => '_section',
						'value' => $hook,
						'compare' => '='
					);
				}
				$posts = get_posts($args);
				$this->_product_fields->cache_set( $cat_cache_key, $posts );
			}
			foreach ($posts as $post) {
				$ret['posts'][$post->ID] = $post;
			}
			$ret['posts'] = apply_filters( 'flexible_product_fields_sort_groups_posts', $ret['posts'] );
			foreach ( $ret['posts'] as $key => $post ) {
				$ret['posts'][$key]->fields_meta = get_post_meta( $post->ID, '_fields', true );
				if ( is_array( $ret['posts'][$key]->fields_meta ) ) {
					$new_fields = array_map(
						function ( $field_data ) use ( $post ) {
							$field_data['_group_id'] = $post->ID;
							return $field_data;
						},
						$ret['posts'][$key]->fields_meta
					);
					$ret['fields'] = array_merge($ret['fields'], $new_fields);
				}
			}
			foreach ( $ret['fields'] as $key => $field ) {
				$field_type = $this->get_field_type( $field['type'] );
				$ret['fields'][$key]['has_price'] = isset( $field_type['has_price'] ) ? $field_type['has_price'] : false;
				$ret['fields'][$key]['has_price_in_options'] = isset( $field_type['has_price_in_options'] ) ? $field_type['has_price_in_options'] : false;
				$ret['fields'][$key]['has_options'] = $field_type['has_options'];
				if ( empty( $field_type['is_available'] ) && !$field_type['is_available'] ) {
					unset( $ret['fields'][$key] );
				}
				else {
					if ( isset( $field['required'] ) && ( $field['required'] == 1 ) ) {
						$ret['has_required'] = true;
					}
				}
			}
			$this->_product_fields->cache_set( $cache_key, $ret );
		}
		return $ret;
	}

	/**
	 * @param WC_Product $product
	 *
	 * @return array
	 */
    public function get_logic_rules_for_product( $product ) {
        $fields = $this->get_translated_fields_for_product( $product );
	    $rules = array();
        foreach ( $fields['fields'] as $field ) {
            if ( isset( $field['logic'] ) && $field['logic'] == '1' && isset( $field['logic_operator'] ) && isset( $field['logic_rules'] ) ) {
                $rules[$field['id']] = array();
	            $rules[$field['id']]['rules'] = $field['logic_rules'];
	            $rules[$field['id']]['operator'] = $field['logic_operator'];
            }
        }
        return $rules;
    }

	/**
	 * @param WC_Product $product
	 * @param bool|string $hook
	 *
	 * @return array
	 */
    public function create_fields_for_product( $product, $hook ) {
        $fields = $this->get_translated_fields_for_product( $product, $hook );
        foreach ( $fields['fields'] as $field ) {
            $fields['display_fields'][] = $this->create_field( $field, $product );
        }
		return $fields;
    }

	/**
	 * @param array $field
	 * @param WC_Product $product
	 *
	 * @return string
	 */
    public function create_field( array $field, WC_Product $product ) {
        $field_type    = $this->get_field_type( $field['type'] );
		$template_vars = $field_type['type_object']->get_field_template_vars( $field );

		$template_vars['value'] = $field_type['type_object']->get_field_value( $field['id'], true );
		$template_vars['args']  = ( new TemplateArgs() )->parse_field_args(
			$field_type,
			$field_type['type_object'],
			$field,
			$this->product_price,
			$product
		);

		return $this->_plugin->load_template(
			$field_type['template_file'],
			'fields',
			$template_vars
		);
    }

	/**
	 * @param bool|string $hook
	 */
    public function show_fields( $hook ) {
        global $product;
        $fields = $this->create_fields_for_product( $product, $hook );
        if ( count( $fields['display_fields'] ) ) {
            echo $this->_plugin->load_template( $hook, 'hooks', array( 'fields' => $fields['display_fields'] ) );
        }
    }

	/**
     * Translate fields titles and labels.
     *
	 * @param array $fields
	 *
	 * @return array
	 */
    private function translate_fields_titles_and_labels( array $fields ) {
	    foreach ( $fields['fields'] as $key => $field ) {
	        $field['title'] = wpdesk__( $field['title'], 'flexible-product-fields' );
	        if ( isset( $field['placeholder'] ) ) {
		        $field['placeholder'] = wpdesk__( $field['placeholder'], 'flexible-product-fields' );
	        }
		    if ( $field['has_options'] ) {
			    foreach ( $field['options'] as $option_key => $option ) {
				    $field['options'][ $option_key ]['label'] = wpdesk__( $option['label'], 'flexible-product-fields' );
			    }
		    }
		    $fields['fields'][ $key ] = $field;
        }
        return $fields;
    }

	/**
	 * Fired by woocommerce_before_add_to_cart_button hook.
	 */
    public function woocommerce_before_add_to_cart_button() {
        /** Prevent display fields more than once. Action may be fired by other third party plugins, ie. Woocommerce Subscriptions */
        if ( $this->is_woocommerce_before_add_to_cart_button_fired ) {
            return;
        }
	    $this->is_woocommerce_before_add_to_cart_button_fired = true;
        global $product;
	    $product_extended_info = new FPF_Product_Extendend_Info( $product );
        if ( $product_extended_info->is_type_grouped() ) {
            return;
        }
        $this->show_fields( 'woocommerce_before_add_to_cart_button' );
        echo $this->_plugin->load_template( 'display', 'totals', array() );
        $fields = $this->translate_fields_titles_and_labels( $this->get_translated_fields_for_product( $product ) );
        foreach ( $fields['fields'] as $key => $field ) {
            $fields['fields'][$key]['price_value'] = 0;
            if ( !isset( $field['price_type'] ) ) {
	            $field['price_type'] = 'fixed';
	            $fields['fields'][$key]['price_type'] = 'fixed';
            }
            if ( $field['has_price'] && isset($field['price_type']) && $field['price_type'] != '' && isset($field['price']) && $field['price'] != '' ) {
            	$price_value = $this->product_price->calculate_price( floatval($field['price']), $field['price_type'], $product );
                $fields['fields'][$key]['price_value'] = $price_value;
	            $fields['fields'][$key]['price_display'] = $this->product_price->prepare_price_to_display( $product, $price_value );
            }
            if ( $field['has_options'] ) {
				foreach ( $fields['fields'][$key]['options'] as $option_key => $option ) {
                    $fields['fields'][ $key ]['options'][ $option_key ]['price_value'] = 0;
	                if ( ! $field['has_price_in_options'] ) {
						continue;
					}

            		$price_values       = $field['price_values'] ?? [];
	                $option_price_type  = $price_values[ $option['value'] ]['price_type'] ?? ( $option['price_type'] ?? '' );
	                $option_price_value = $price_values[ $option['value'] ]['price'] ?? ( $option['price'] ?? '' );
	                if ( ( $option_price_type === '' ) || ( $option_price_value === '' ) ) {
	                	continue;
					}

					$price_value = $this->product_price->calculate_price( floatval($option_price_value ), $option_price_type, $product );
					$fields['fields'][ $key ]['options'][ $option_key ]['price_type']    = $option_price_type;
					$fields['fields'][ $key ]['options'][ $option_key ]['price']         = $option_price_value;
					$fields['fields'][ $key ]['options'][ $option_key ]['price_value']   = $price_value;
					$fields['fields'][ $key ]['options'][ $option_key ]['price_display'] = $this->product_price->prepare_price_to_display( $product, $price_value );
                }
			}
        }
        $tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
        if ( $tax_display_mode == 'excl' ) {
	        $product_price = wpdesk_get_price_excluding_tax( $product );
        }
        else {
	        $product_price = wpdesk_get_price_including_tax( $product );
        }
		?>
        <script type="text/javascript">
            var fpf_fields = <?php echo json_encode( $fields['fields'] ); ?>;
            var fpf_product_price = <?php echo json_encode( $product_price ); ?>;
        </script>
        <?php
    }

	/**
	 *
	 */
    public function woocommerce_after_add_to_cart_button() {
	    global $product;
	    $product_extended_info = new FPF_Product_Extendend_Info( $product );
	    if ( $product_extended_info->is_type_grouped() ) {
		    return;
	    }
        $this->show_fields( 'woocommerce_after_add_to_cart_button' );
    }


}