* */ class AffiGet_Review_Element_Pricing_Details extends AffiGet_Abstract_Element { protected $map_conditions_to_attributes = array( 'new' => 'LowestNewPrice', 'used' => 'LowestUsedPrice', 'collectible' => 'LowestCollectiblePrice', 'refurbished' => 'LowestRefurbishedPrice', ); function __construct( AffiGet_Abstract_Meta $meta, $name, array $params ){ parent::__construct( $meta, $name, $params ); //calls resolve_settings(), which calls get_settings_config() if( ! $this->is_status( AffiGet_Abstract_Element::STATUS_ENABLED ) ) return; $element_name = $this->name; add_action("afg_front__html_{$element_name}", array(&$this, 'front_html'), 10, 1); //metavalue $meta_key = AFG_META_PREFIX . $this->name; add_filter( "sanitize_post_meta_{$meta_key}", array(&$this, 'sanitize_value' ), 10, 3); //metabox add_action( 'afg_review_renderer__register_metabox_fields', array(&$this, 'register_cmb2_fields')); add_action( 'cmb2_render_afg_pricing_details', array(&$this, 'render_cmb2_field'), 10, 5 ); //assets add_action( 'admin_enqueue_scripts', array(&$this, 'enqueue_scripts_and_styles')); add_action( 'wp_enqueue_scripts', array(&$this, 'enqueue_scripts_and_styles')); } protected function get_settings_config(){ $new_fields = array( 'allow_conditions' => array( 'name' => 'allow_conditions', 'atts' => 'size="55"', 'type' => 'checklist', 'options' => array( 'new' => __('New', 'afg'), 'used' => __('Used', 'afg'), 'collectible' => __('Collectible', 'afg'), 'refurbished' => __('Refurbished', 'afg'), ), 'default' => 'new', 'label' => __('Allowed conditions', 'afg'), 'hint' => __('A list of conditions to take into account when calculating the best price.', 'afg'), 'help' => __('Help'), ), 'show_details' => array( 'name' => 'show_details', 'atts' => 'size="55"', 'type' => 'checklist', 'options' => array( 'list_price' => __('List price','afg'), 'best_price' => __('Best price','afg'), 'saved_amount' => __('Saved amount','afg'), 'saved_percentage' => __('Saved percentage','afg'), 'condition' => __('Condition','afg'), 'items_in_store' => __('Items in store','afg'), 'offer_details' => __('Offer details','afg'), 'variant_details' => __('Variant details','afg'), ), 'default' => 'list_price,best_price,saved_amount,saved_percentage,condition,items_in_store,offer_details,variant_details', 'label' => __('Allowed conditions', 'afg'), 'hint' => __('A list of conditions to take into account when calculating the best price.', 'afg'), 'help' => __('Help'), ), 'display_format' => array( 'name' => 'display_format', 'atts' => '', 'type' => 'dropdown', 'options' => array( 'table' => __('Table - labels in first column (default)', 'afg'), /* labels in the first column */ 'list' => __('List - labels in front of values', 'afg'), /* labels before values */ 'form' => __('Form - small labels below values', 'afg'), /* small labels under values */ 'divs' => __('Generic - divs and spans', 'afg'), /* divs and spans */ ), 'default' => 'list', 'label' => __('', 'afg'), 'hint' => __('', 'afg'), 'help' => __('Help'), ), ); return array_merge( parent::get_settings_config(), $new_fields ); } function register_cmb2_fields( &$fields ){ /*$fields[] = array( 'name' => $this->settings['title'], //'desc' => __('Title to show on the front end.', 'afg'), 'id' => AFG_META_PREFIX . $this->name .'_heading', 'type' => 'title', 'position'=> $this->settings[ 'metabox_position' ], //'default' => $this->settings['title'], );*/ $post_type_name = $this->meta->post_type_name; $fields[] = array( 'name' => 'value', 'desc' => $this->settings['description'], 'id' => AFG_META_PREFIX . $this->name, 'type' => 'afg_pricing_details', //'options' => array( 'textarea_rows' => $this->settings[ 'textarea_rows' ] ), 'position'=> $this->settings[ 'metabox_position' ], 'metabox' => array( 'id' => $this->control_id. '_metabox', 'title' => $this->settings['label'], 'object_types' => array( $post_type_name ), // Post type 'context' => 'side', 'priority' => 'low', //high/core/default/low 'show_names' => false, // Show field names on the left // 'cmb_styles' => false, // false to disable the CMB stylesheet // 'closed' => true, // true to keep the metabox closed by default ) ); } function front_html( array $review_data ){ $post_id = $review_data['post_fields']['ID']; /*$nonce = ''; if ( current_user_can('edit_post', $post_id )){ $nonce = wp_create_nonce("afg-update-{$post_id}"); }*/ ob_start(); $this->render_html( $post_id, null, $this->name, $this->control_id, '' ); $content = ob_get_clean(); if( $content ){ return $this->front_title() . $content; } return; } function render_cmb2_field( $field_args, $value, $post_id, $object_type, $field_type_object ){ if( $field_args->args['id'] === AFG_META_PREFIX . $this->name ){ $nonce = ''; if ( current_user_can('edit_post', $post_id )){ $nonce = wp_create_nonce("afg-update-{$post_id}"); } $this->render_html( $post_id, null, $this->name, AFG_META_PREFIX . $this->name, $nonce ); } } protected function _prepare_items( &$items, $post_id, $fieldname ){ $encoded = false; if( is_null( $items )){ if( $post_id && $fieldname == $this->name ){ $review_data = $this->meta->load_post( $post_id ); $items = $this->meta->pick_value( $review_data, $fieldname ); } if( empty( $items )){ $items = $this->get_default_value( compact( 'post_id', 'fieldname' ) ); } $encoded = json_encode( $items ); } elseif( is_string( $items )){ $encoded = $items; $items = json_decode( $items, $assoc = true ); } else { $encoded = json_encode( $items ); } return $encoded; } function render_html( $post_id, $items, $fieldname, $input_id, $nonce, $context = 'not-widget', $params = null ){ $format = $this->settings['display_format']; printf('
', 'afg-pricing-details', 'contains-'.$format, $context, $post_id, $fieldname, $nonce, $input_id, isset( $params['wid'] ) ? $params['wid'] : '' ); $encoded = $this->_prepare_items( $items, $post_id, $fieldname ); //echo '
Items:['.print_r( $items, true) . '
'; if( 'widget-settings' != $context ){ printf("", $input_id, $this->control_id, $encoded ); } $this->render_content( $items ); echo('
'); } function get_preview( $post_id, $options ){ $items = $this->get_default_value_for_post( $post_id, array('widget_data' => $options )); echo '
'; $this->render_content( $items ); echo ""; echo '
'; } protected function render_content( $items, $echo = true ){ if( empty( $items )){ $content = '
'; if( $echo ){ echo $content; } return $content; } $prices = $items['prices']; $labels = $items['labels']; $options = $items['options']; $html = ''; $show_details = $options['show_details']; if( ! isset( $labels['list_price'] )){ return ''; //echo '
';
			//throw new AffiGet_Exception('No labels!');
		}

		if( $labels['list_price'] ){

			$price_part = sprintf(
					_x('$%s', 'localized price display', 'afg'),
					number_format( $prices['list_price'], 2 )
			);

			if( in_array( 'best_price', $show_details ) && $prices['best_price'] && $prices['list_price'] !== $prices['best_price'] ){
				$price_part = sprintf('%s', $price_part );
			} else {
				$price_part = sprintf('%s', $price_part );
			}

			$html = sprintf( "
  • {$labels['list_price']}
  • ", $price_part ); } if( $labels['best_price'] ){ $price_part = sprintf( _x('$%s', 'localized price display', 'afg'), number_format( $prices['best_price'], 2 ) ); $price_part = sprintf('%s', $price_part ); $condition = ''; if( $labels['condition'] ){ $condition .= " {$labels['condition']}"; } $html .= sprintf( "
  • {$labels['best_price']}
  • ", $price_part, $condition ); } if( $labels['saved'] ){ $saved = ''; $amount = ''; $percent = ''; if( in_array( 'saved_amount', $show_details ) ){ $amount = sprintf( '$%s', number_format( $prices['saved_amount'], 2 ) ); } if( in_array( 'saved_percentage', $show_details )){ if( $amount ){ //with parentheses $percent = sprintf( ' (%s%)', number_format( $prices['saved_percentage'], 1) ); } else { //with no parentheses $percent = sprintf( '%s%', number_format( $prices['saved_percentage'], 1) ); } } $html .= sprintf( "
  • {$labels['saved']}
  • ", $amount, $percent ); } $content = "
    "; if( $echo ){ echo $content; } return $content; } function direct_update( $post_id, $fieldname, $items, $part_id = 0 ){ if( is_string( $items )){ $items = json_decode( $items, true ); } $base_meta_key = AFG_META_PREFIX . $this->name; //XXX here we should use declared_meta_field to be more correct if( $this->name == $fieldname ){ $result = $this->repick_product_prices( $items, $post_id, $items['options'] ); if( $result ){ $result = update_post_meta( $post_id, $base_meta_key, $items ); } } else { if( ! $part_id ){ $items = $this->sanitize_value( $items, $base_meta_key, 'post' ); $result = $this->repick_product_prices( $items, $post_id, $items['options'] ); if( $result ){ return null; } //most likely this comes from a standard widget $postfix = preg_replace("/[^0-9]/", "", $fieldname ); //remove all non-numbers $result = update_post_meta( $post_id, $base_meta_key . '_' . $postfix, $items ); } else { //most likely this is a widget controlled by PageBuilder //it stores all of its layout definition in a specific post meta $panels_data = get_post_meta( $post_id, 'panels_data', true ); if( $panels_data && !empty( $panels_data )){ $widget = false; $idx = -1; foreach( $panels_data['widgets'] as &$w ){ $idx++; if( isset( $w['wid'] ) && $w['wid'] == $part_id ){ $widget = &$w; break; } } if( $widget ){ $items = $this->sanitize_value( $items, $base_meta_key, 'post' ); $result = $this->repick_product_prices( $items, $post_id, $items['options'] ); if( $result ){ return null; } $widget['items'] = json_encode( $items ); $result = update_post_meta( $post_id, 'panels_data', $panels_data ); if( ! $result ){ afg_log(__METHOD__, 'could not update panels_data on ' . $post_id. ' with '.print_r($panels_data, true) ); } } else { afg_log(__METHOD__, 'widget not found ' . $part_id); //echo 'could not find wid '.$part_id; $result = false; } } } } return $result; } //to be hooked into sanitize_meta(), but can also be called directly function sanitize_value( $items, $meta_key, $meta_type ){ if( false === strpos( $meta_key, AFG_META_PREFIX . $this->name )){ //return $meta_value; throw new AffiGet_Exception('Unexpected meta_key: ' . $meta_key ); } if( is_string( $items )){ $items = json_decode( $items, $assoc = true ); } $cleaned = array( 'options' => array( 'allow_conditions' => array(), 'show_details' => array(), ), 'prices' => array(), 'labels' => array(), ); if( is_array( $items )){ if( is_array( $items['options'] )){ if( is_array( $items['options']['allow_conditions'] )){ $cleaned['options']['allow_conditions'] = $items['options']['allow_conditions']; } if( is_array( $items['options']['show_details'] )){ $cleaned['options']['show_details'] = $items['options']['show_details']; } } if( is_array( $items['prices'] )){ $cleaned['prices'] = $items['prices']; } if( is_array( $items['labels'] )){ $cleaned['labels'] = $items['labels']; } } if( empty( $cleaned['options']['allow_conditions'] )){ $cleaned['options']['allow_conditions'][] = 'new'; } if( empty( $cleaned['options']['show_details'] )){ $cleaned['options']['show_details'][] = 'list_price'; } return $cleaned; } protected function repick_product_prices( &$items, $post_id, $options ){ $product_data = get_post_meta( $post_id, AFG_META_PREFIX . 'product_data', true ); if( ! $product_data ){ return null; } $items['prices'] = $this->prepare_prices( $product_data, $options['allow_conditions'] ); $items['labels'] = $this->prepare_labels( $items['prices'], $options['show_details'] ); //afg_log(__METHOD__, compact('items', 'options')); return true; } protected function prepare_labels( $prices, $show_details ){ $labels = array( 'list_price' => '', 'best_price' => '', 'condition' => '', 'saved' => '', ); if( in_array( 'list_price', $show_details ) && $prices['list_price'] ){ if( in_array( 'best_price', $show_details ) && $prices['best_price'] && $prices['list_price'] !== $prices['best_price'] ){ $labels['list_price'] = __('Regular price: %s', 'afg'); } else { $labels['list_price'] = __('Price from: %s', 'afg'); } } if( in_array( 'best_price', $show_details ) && $prices['best_price'] && $prices['list_price'] !== $prices['best_price']){ if( ! $prices['list_price']){ $labels['best_price'] = _x('Price from: %1$s%2$s', 'price, condition', 'afg'); } else { $labels['best_price'] = _x('Current offer: %1$s%2$s', 'price, condition', 'afg'); } $conditions = array( //'' => __('','afg'), 'special' => __('(special)', 'afg'), 'new' => __('(new)', 'afg'), 'used' => __('(used)', 'afg'), 'refurbished' => __('(refurbished)', 'afg'), 'collectible' => __('(collectible)', 'afg'), 'variant' => __('(variant)', 'afg'), ); if( in_array( 'condition', $show_details ) && array_key_exists( $prices['condition'], $conditions )){ $labels['condition'] = $conditions[ $prices['condition'] ]; } } if( in_array( 'saved_amount', $show_details ) || in_array( 'saved_percentage', $show_details )){ if( $prices['best_price'] && $prices['list_price'] && $prices['best_price'] < $prices['list_price'] ){ $labels['saved'] = _x('You save: %1$s %2$s', 'saved amount, saved percentage', 'afg'); } } return apply_filters('afg_pricing_details__prepare_labels', $labels, $prices, $show_details ); } protected function prepare_prices( $product_data, $allow_conditions ){ //find min price $min_amount = PHP_INT_MAX; $min_condition = ''; $min_currency = ''; foreach( $allow_conditions as $condition ){ if( array_key_exists( $condition, $this->map_conditions_to_attributes )){ $price = AffiGet_Review_Meta::pick_product_data_value( $product_data, $this->map_conditions_to_attributes[ $condition ] ); if( $price['Amount'] && $price['Amount'] < $min_amount ){ $min_amount = $price['Amount']; $min_condition = $condition; $min_currency = $price['CurrencyCode']; } } } $list_price = AffiGet_Review_Meta::pick_product_data_value( $product_data, 'ListPrice' ); $list_amount = $list_price['Amount']; if( ! $list_amount ){ if( $min_amount != PHP_INT_MAX ){ $list_amount = $min_amount; } else { $list_amount = 0; } } $saved_amount = 0; $saved_percent = 0; if( PHP_INT_MAX != $min_amount && $list_amount != $min_amount){ //$times = $saved_amount * 100; //afg_log(__METHOD__, compact('list_amount', 'min_amount', 'times')); $saved_amount = $list_amount - $min_amount; $saved_percent = round(( $saved_amount * 100 ) / $list_amount, 2 ); } else { $min_amount = $list_amount; } $result = array( 'list_price' => $list_amount ? ($list_amount / 100) : 0, 'best_price' => $min_amount ? ($min_amount / 100): 0, 'currency' => $min_currency, 'saved_amount' => $saved_amount ? ($saved_amount / 100): 0, 'saved_percentage' => $saved_percent, 'condition' => $min_condition, 'items_in_store' => -1, 'offer_details' => '', 'variant_details' => '', ); return $result; } protected function get_default_value_for_post( $post_id, $params ){ $preset = (isset( $params['widget_data'] ) && $params['widget_data']) ? $params['widget_data'] : false; $items = array( 'options' => $preset ? $preset['options'] : $params['defaults']['options'] ); $result = $this->repick_product_prices( $items, $post_id, $items['options'] ); return $result ? $items: null; } function get_default_value( $params = null ){ //get default from settings $defaults = array( 'options' => array( 'allow_conditions' => array_map('trim', explode(',', $this->settings['allow_conditions'] )), 'show_details' => array_map('trim', explode(',', $this->settings['show_details'] )), ), 'prices' => array( 'list_price' => 12.34, 'best_price' => 7.89, 'saved_amount' => 12.34-7.89, 'saved_percentage' => ((12.34-7.89)*100)/12.34, 'currency' => 'USD', 'condition' => 'used', 'items_in_store' => '10', 'offer_details' => '', 'variant_details' => '', ), 'labels' => null ); //get default from post (can be merged with widget settings that are passed as one of the params) if( isset( $params['post_id'] ) && 0 < absint( $params['post_id'] )){ $params['defaults'] = $defaults; $result = $this->get_default_value_for_post( absint( $params['post_id'] ), $params ); if( $result ){ return $result; } } $defaults['labels'] = $this->prepare_labels( $defaults['prices'], $defaults['options']['show_details'] ); //afg_log(__METHOD__, compact('params', 'defaults')); return $defaults; } function enqueue_scripts_and_styles( $hook ) { if( $this->meta->is_review_style_needed() ){ wp_enqueue_style( 'afg-pricing-details-style', plugins_url( '/css/element.css', (__FILE__)), array(), AFG_VER ); } if( $this->meta->is_review_script_needed() ){ wp_enqueue_script( 'afg-pricing-details-script', plugins_url( '/js/element.js', (__FILE__)), array('afg-raty-script'), AFG_VER ); } } // end enqueue_scripts_and_styles } endif; /* EOF */