oont-contents/plugins/webappick-product-feed-for-woocommerce/V5/Helper/FeedHelper.php
2025-03-31 21:42:48 +02:00

1890 lines
60 KiB
PHP

<?php
/**
* @package CTXFeed\V5\Helper
*/
namespace CTXFeed\V5\Helper;
use CTXFeed\V5\Common\Helper;
use CTXFeed\V5\FTP\FtpClient;
use CTXFeed\V5\FTP\FtpException;
use CTXFeed\V5\Product\AttributeValueByType;
use CTXFeed\V5\Query\QueryFactory;
use CTXFeed\V5\Template\TemplateFactory;
use CTXFeed\V5\Utility\Cache;
use CTXFeed\V5\Utility\Config;
use CTXFeed\V5\Utility\FileSystem;
use CTXFeed\V5\Utility\Settings;
use WP_Error;
/**
* This class contains feed generated method
*/
class FeedHelper {
/**
* Sanitizes form fields recursively using WordPress standards.
*
* @param array $data Data associated with form fields to be sanitized.
*
* @return array Sanitized form data.
*/
public static function sanitize_form_fields( $data ) {
foreach ( $data as $k => $v ) {
if ( true === apply_filters( 'woo_feed_sanitize_form_fields', true, $k, $v, $data ) ) {
if ( is_array( $v ) ) {
$v = self::sanitize_form_fields( $v );
} else {
// $v = sanitize_text_field( $v ); #TODO should not trim Prefix and Suffix field
}
}
$data[ $k ] = apply_filters( 'woo_feed_sanitize_form_field', $v, $k );
}
return $data;
}
/**
* Generates a unique filename for a feed, ensuring no conflicts in the feed directory.
*
* @param string $file_name The initial filename.
* @param string $type The type of the feed (e.g., 'xml', 'csv').
* @param string $provider The provider for which the feed is being generated.
*
* @return string|false The unique filename, or false if an error occurs.
*/
public static function generate_unique_feed_file_name( $file_name, $type, $provider ) {
if ( ! \is_string( $file_name ) || ! \is_string( $type ) || ! \is_string( $provider ) ) {
// Handle invalid input types.
return false;
}
$feed_dir = Helper::get_file_dir( $provider, $type );
$raw_filename = \sanitize_title( $file_name, '', 'save' );
$raw_filename = self::unique_feed_slug( $raw_filename, 'wf_feed_' );
$raw_filename = \sanitize_file_name( $raw_filename . '.' . $type );
$raw_filename = \wp_unique_filename( $feed_dir, $raw_filename );
$base_filename = \str_replace( '.' . $type, '', $raw_filename );
return \is_numeric( $base_filename ) ? false : $base_filename;
}
/**
* Generates a unique slug for a feed by checking against existing database entries.
* This function delegates to CommonHelper::unique_option_name for actual uniqueness check.
* Use generate_unique_feed_file_name() for a complete unique file name generation.
*
* @param string $slug The initial slug for the feed.
* @param string $prefix An optional prefix to prepend to the slug.
* @param int $option_id An optional ID to exclude a specific option from the uniqueness check.
*
* @return string Unique slug for the feed.
* @see CommonHelper::unique_option_name()
*/
public static function unique_feed_slug( $slug, $prefix = '', $option_id = null ) {
return CommonHelper::unique_option_name( $slug, $prefix, $option_id );
}
/**
* Sanitizes and saves feed configuration data to the WordPress options table.
*
* @param array $feed_rules Data to be saved. Should be an associative array of feed rules.
* @param string|null $feed_option_name Optional. The name of the feed option. If null, a name is auto-generated.
* @param bool $configOnly Optional. Whether to save only 'wf_config' or both 'wf_config' and 'wf_feed_'. Defaults to true.
*
* @return bool|string False on failure, or the feed option name on success.
*/
public static function save_feed_config_data( $feed_rules, $feed_option_name = null, $configOnly = true ) {
if ( ! \is_array( $feed_rules ) ) {
// Handle invalid input
return false;
}
$prepared_feed_rules = self::prepare_feed_rules_to_save( $feed_rules, $feed_option_name );
if ( ! $prepared_feed_rules ) {
// Handle failure in preparing feed rules
return false;
}
$feed_option_name = $prepared_feed_rules['feed_option_name'];
$is_update = $prepared_feed_rules['is_update'];
self::call_action_before_update_feed_config( $is_update, $feed_rules, $feed_option_name );
$updated = update_option( $feed_option_name, $prepared_feed_rules['feedrules_to_save'], false );
self::call_action_after_update_feed_config( $is_update, $feed_rules, $feed_option_name );
// Return feed option name on success or false if update failed
return $updated ? $feed_option_name : false;
}
/**
* Prepares feed rules for saving to the database, ensuring data integrity and sanitization.
*
* @param array $feed_rules Data to be saved, expected to contain 'filename', 'feedType', 'provider'.
* @param mixed $feed_option_name Optional. Feed name, auto-generated if null or empty.
*
* @return array|false Returns prepared data for saving or false if input is invalid.
*/
public static function prepare_feed_rules_to_save( $feed_rules, $feed_option_name ) {
if ( ! \is_array( $feed_rules ) || ! self::validate_feed_rules( $feed_rules ) ) {
return false;
}
$feed_rules = self::remove_unnecessary_fields( $feed_rules );
$feed_rules = self::sanitize_form_fields( $feed_rules );
// Handle feed option name generation or retrieval.
list( $feed_option_name, $old_feed, $update, $status ) = self::handle_feed_option_name( $feed_rules, $feed_option_name );
$feed_url = self::get_file_url( $feed_option_name, $feed_rules['provider'], $feed_rules['feedType'] );
// Modify feed rules before save to database.
$feed_rules = apply_filters( 'woo_feed_insert_feed_data', $feed_rules, $old_feed, $feed_option_name );
$feed_rulesToSave = [
'feedrules' => $feed_rules,
'url' => $feed_url,
'last_updated' => \current_time( 'mysql' ),
'status' => $status,
];
return [
'feedrules_to_save' => $feed_rulesToSave,
'is_update' => $update,
'old_data' => $old_feed,
'feed_option_name' => $feed_option_name,
];
}
/**
* Validates the required keys in feed rules.
*
* @param array $feed_rules
*
* @return bool
*/
private static function validate_feed_rules( $feed_rules ) {
return isset( $feed_rules['filename'], $feed_rules['feedType'], $feed_rules['provider'] );
}
/**
* Removes unnecessary fields from feed rules.
*
* @param array $feed_rules
*
* @return array
*/
private static function remove_unnecessary_fields( $feed_rules ) {
// Define fields to remove
$removables = array( 'closedpostboxesnonce', '_wpnonce', '_wp_http_referer', 'save_feed_config', 'edit-feed' );
foreach ( $removables as $removable ) {
unset( $feed_rules[ $removable ] );
}
return $feed_rules;
}
/**
* Handles the generation or retrieval of feed option name.
*
* @param array $feed_rules
* @param mixed $feed_option_name
*
* @return array
*/
private static function handle_feed_option_name( $feed_rules, $feed_option_name ) {
if ( empty( $feed_option_name ) ) {
$feed_option_name = AttributeValueByType::FEED_RULES_OPTION_PREFIX . self::generate_unique_feed_file_name(
$feed_rules['filename'],
$feed_rules['feedType'],
$feed_rules['provider']
);
return [ $feed_option_name, array(), false, 1 ];
} else {
$old_feed = maybe_unserialize( get_option( $feed_option_name, [] ) );
$status = isset( $old_feed['status'] ) && 1 === (int) $old_feed['status'] ? 1 : 0;
return [ $feed_option_name, $old_feed, true, $status ];
}
}
/**
* @param $update
* @param $feed_rules
* @param $feed_option_name
*
* @return void
*/
public static function call_action_before_update_feed_config( $update, $feed_rules, $feed_option_name ) {
if ( $update ) {
/**
* Before Updating Config to db
*
* @param array $feed_rules An array of sanitized config
* @param string $feed_option_name Option name
*/
do_action( 'woo_feed_before_update_config', $feed_rules, $feed_option_name );
} else {
/**
* Before inserting Config to db
*
* @param array $feed_rules An array of sanitized config
* @param string $feed_option_name Option name
*/
do_action( 'woo_feed_before_insert_config', $feed_rules, $feed_option_name );
}
}
/**
* @param $update
* @param $feed_rules
* @param $feed_option_name
*
* @return void
*/
public static function call_action_after_update_feed_config( $update, $feed_rules, $feed_option_name ) {
if ( $update ) {
/**
* After Updating Config to db
*
* @param array $feed_rules An array of sanitized config
* @param string $feed_option_name Option name
*/
do_action( 'woo_feed_after_update_config', $feed_rules, $feed_option_name );
} else {
/**
* After inserting Config to db
*
* @param array $feed_rules An array of sanitized config
* @param string $feed_option_name Option name
*/
do_action( 'woo_feed_after_insert_config', $feed_rules, $feed_option_name );
}
}
/**
* @param $rules
* @param $context
*
* @return mixed|null
*/
public static function parse_feed_rules( $rules = array(), $context = 'view' ) {
if ( empty( $rules ) ) {
$rules = array();
}
$defaults = Config::default_feed_rules();
$rules = wp_parse_args( $rules, $defaults );
$rules['filter_mode'] = wp_parse_args(
$rules['filter_mode'],
array(
'product_ids' => 'include',
'categories' => 'include',
'post_status' => 'include',
)
);
$rules['campaign_parameters'] = wp_parse_args(
$rules['campaign_parameters'],
array(
'utm_source' => '',
'utm_medium' => '',
'utm_campaign' => '',
'utm_term' => '',
'utm_content' => '',
)
);
if ( ! empty( $rules['provider'] ) && is_string( $rules['provider'] ) ) {
/**
* filter parsed rules for provider
*
* @param array $rules
* @param string $context
*
* @since 3.3.7
*
*/
$rules = apply_filters( "woo_feed_{$rules['provider']}_parsed_rules", $rules, $context );
}
/**
* filter parsed rules
*
* @param array $rules
* @param string $context
*
* @since 3.3.7 $provider parameter removed
*
*/
return apply_filters( 'woo_feed_parsed_rules', $rules, $context );
}
/**
* Get pro version feed default rules.
*
* @param $rules
*
* @return mixed|null
*/
private static function free_default_feed_rules( $rules = [] ) {
$defaults = array(
'provider' => '',
'filename' => '',
'feedType' => '',
'feed_country' => '',
'ftpenabled' => 0,
'ftporsftp' => 'ftp',
'ftphost' => '',
'ftpport' => '21',
'ftpuser' => '',
'ftppassword' => '',
'ftppath' => '',
'ftpmode' => 'active',
'is_variations' => 'y',
'variable_price' => 'first',
'variable_quantity' => 'first',
'feedLanguage' => apply_filters( 'wpml_current_language', null ),
'feedCurrency' => get_woocommerce_currency(),
'itemsWrapper' => 'products',
'itemWrapper' => 'product',
'delimiter' => ',',
'enclosure' => 'double',
'extraHeader' => '',
'vendors' => array(),
// Feed Config
'mattributes' => array(), // merchant attributes
'prefix' => array(), // prefixes
'type' => array(), // value (attribute) types
'attributes' => array(), // product attribute mappings
'default' => array(), // default values (patterns) if value type set to pattern
'suffix' => array(), // suffixes
'output_type' => array(), // output type (output filter)
'limit' => array(), // limit or command
// filters tab
'composite_price' => '',
'shipping_country' => '',
'tax_country' => '',
'product_ids' => '',
'categories' => array(),
'post_status' => array( 'publish' ),
'filter_mode' => array(),
'campaign_parameters' => array(),
'ptitle_show' => '',
'decimal_separator' => wc_get_price_decimal_separator(),
'thousand_separator' => wc_get_price_thousand_separator(),
'decimals' => wc_get_price_decimals(),
);
$rules = wp_parse_args( $rules, $defaults );
return apply_filters( 'woo_feed_free_default_feed_rules', $rules );
}
/**
* Get pro version feed default rules.
*
* @param $rules
*
* @return mixed|null
*/
private static function pro_default_feed_rules( $rules = [] ) {
$defaults = array(
'provider' => '',
'feed_country' => '',
'filename' => '',
'feedType' => '',
'ftpenabled' => 0,
'ftporsftp' => 'ftp',
'ftphost' => '',
'ftpport' => '21',
'ftpuser' => '',
'ftppassword' => '',
'ftppath' => '',
'ftpmode' => 'active',
'is_variations' => 'y', // Only Variations (All Variations)
'variable_price' => 'first',
'variable_quantity' => 'first',
'feedLanguage' => apply_filters( 'wpml_current_language', null ),
'feedCurrency' => get_woocommerce_currency(),
'itemsWrapper' => 'products',
'itemWrapper' => 'product',
'delimiter' => ',',
'enclosure' => 'double',
'extraHeader' => '',
'vendors' => array(),
// Feed Config
'mattributes' => array(), // merchant attributes
'prefix' => array(), // prefixes
'type' => array(), // value (attribute) types
'attributes' => array(), // product attribute mappings
'default' => array(), // default values (patterns) if value type set to pattern
'suffix' => array(), // suffixes
'output_type' => array(), // output type (output filter)
'limit' => array(), // limit or command
// filters tab
'composite_price' => 'all_product_price',
'product_ids' => '',
'categories' => array(),
'post_status' => array( 'publish' ),
'filter_mode' => array(),
'campaign_parameters' => array(),
'is_outOfStock' => 'n',
'is_backorder' => 'n',
'is_emptyDescription' => 'n',
'is_emptyImage' => 'n',
'is_emptyPrice' => 'n',
'product_visibility' => 0,
// include hidden ? 1 yes 0 no
'outofstock_visibility' => 0,
// override wc global option for out-of-stock product hidden from catalog? 1 yes 0 no
'ptitle_show' => '',
'decimal_separator' => wc_get_price_decimal_separator(),
'thousand_separator' => wc_get_price_thousand_separator(),
'decimals' => wc_get_price_decimals(),
);
// $defaults = [
// "provider" => "",
// "feed_country" => "",
// "filename" => "",
// "feedType" => "xml",
// "ftpenabled" => false,
// "ftporsftp" => "ftp",
// "ftphost" => "",
// "ftpport" => "21",
// "ftpuser" => "",
// "ftppassword" => "",
// "ftppath" => "",
// "ftpmode" => "active",
// "is_variations" => "y",
// "variable_price" => "first",
// "variable_quantity" => "first",
// 'feedLanguage' => apply_filters( 'wpml_current_language', null ),
// 'feedCurrency' => get_woocommerce_currency(),
// "itemsWrapper" => "products",
// "itemWrapper" => "product",
// "delimiter" => ",",
// "enclosure" => "double",
// "extraHeader" => "",
// "vendors" => [],
// "mattributes" => [],
// "prefix" => [],
// "type" => [],
// "attributes" => [],
// "default" => [],
// "suffix" => [],
// "output_type" => [],
// "limit" => [],
// "composite_price" => "all_product_price",
// "product_ids" => [],
// "categories" => [],
// "post_status" => ["publish"],
// "filter_mode" => [
// "product_ids" => "include",
// "categories" => "include",
// "post_status" => "include"
// ],
// "campaign_parameters" => [
// "utm_source" => "",
// "utm_medium" => "",
// "utm_campaign" => "",
// "utm_term" => "",
// "utm_content" => ""
// ],
// "is_outOfStock" => false,
// "is_backorder" => false,
// "is_emptyDescription" => false,
// "is_emptyImage" => false,
// "is_emptyPrice" => false,
// "product_visibility" => false,
// "outofstock_visibility" => false,
// "ptitle_show" => "",
// 'decimal_separator' => wc_get_price_decimal_separator(),
// 'thousand_separator' => wc_get_price_thousand_separator(),
// 'decimals' => wc_get_price_decimals(),
// "feed_option_name" => "",
// "feed_id" => "",
// "meta-box-order-nonce" => "",
// "shipping_country" => "",
// "tax_country" => "",
// "str_replace" => [
// [
// "subject" => "",
// "search" => "",
// "replace" => ""
// ]
// ],
// "concatType" => [],
// "fattribute" => [],
// "condition" => [],
// "filterCompare" => [],
// "wf_tabs" => true,
// "option_id" => "",
// "option_name" => ""
// ];
$rules = wp_parse_args( $rules, $defaults );
return apply_filters( 'woo_feed_pro_default_feed_rules', $rules );
}
/**
* @param $item
* @param $request
*
* @return void|\WP_Error|\WP_REST_Response
*/
public static function prepare_item_for_response( $item ) {
$actual_value_from_db = $item;
if ( isset( $item['option_value'] ) ) {
$item['option_value'] = maybe_unserialize( maybe_unserialize( $item['option_value'] ) );
return apply_filters( 'woo_feed_prepare_item_for_response', $item, $actual_value_from_db );
} else {
$item['option_value'] = maybe_unserialize( get_option( $item['option_name'] ) );
}
if ( ! isset( $item['option_value']['url'] ) ) {
$item['option_value']['url'] = Helper::get_file_url( $item['option_name'], $item['option_value']['feedrules']['provider'], $item['option_value']['feedrules']['feedType'] );
}
if ( ! isset( $item['option_value']['status'] ) ) {
$item['option_value']['status'] = false;
}
return apply_filters( 'woo_feed_prepare_item_for_response', $item, $actual_value_from_db );
}
/**
* @param $feed_lists
* @param $status
*
* @return array
*/
public static function prepare_all_feeds( $feed_lists, $status ) {
$lists = [];
foreach ( $feed_lists as $feed ) {
$item = self::prepare_item_for_response( $feed );
// Skip invalid feed structure
if ( ! self::validate_feed_structure( $item ) ) {
continue;
}
if ( apply_filters( 'woo_feed_should_apply_validate_feed_structure', false ) && ! self::validate_feed_structure( $item, 'full' ) ) {
continue;
}
if ( $status ) {
if ( \is_object( $item['option_value'] ) ) {
$lists[] = $item;
continue;
}
if ( 'active' === $status && 1 === $item['option_value']['status'] ) {
$lists[] = $item;
}
if ( 'inactive' === $status && 0 === $item['option_value']['status'] ) {
$lists[] = $item;
}
} else {
$lists[] = $item;
}
}
// Modify the feed list before returning.
return apply_filters( 'woo_feed_prepare_all_feeds', $lists, $feed_lists, $status );
}
/**
* Validates the structure of a feed option.
*
* @param $feed
* @param $validation_type
*
* @return bool
*/
public static function validate_feed_structure( $feed, $validation_type = 'partial' ) {
// Define the nested structure of required keys
$required_structure = [
'option_id' => 'scalar',
'option_name' => 'scalar',
'option_value' => [
'feedrules' => [
'provider' => 'scalar',
'feed_country' => 'scalar',
'filename' => 'scalar',
'feedType' => 'scalar',
'ftpenabled' => 'scalar',
'ftporsftp' => 'scalar',
'ftphost' => 'scalar',
'ftpport' => 'scalar',
'ftpuser' => 'scalar',
'ftppassword' => 'scalar',
'ftppath' => 'scalar',
'ftpmode' => 'scalar',
'is_variations' => 'scalar',
'variable_price' => 'scalar',
'variable_quantity' => 'scalar',
'feedLanguage' => 'scalar',
'feedCurrency' => 'scalar',
'itemsWrapper' => 'scalar',
'itemWrapper' => 'scalar',
'delimiter' => 'scalar',
'enclosure' => 'scalar',
'extraHeader' => 'scalar',
'vendors' => 'array',
'mattributes' => 'array',
'prefix' => 'array',
'type' => 'array',
'attributes' => 'array',
'default' => 'array',
'suffix' => 'array',
'output_type' => 'array',
'limit' => 'array',
'composite_price' => 'scalar',
'product_ids' => 'array',
'categories' => 'array',
'post_status' => 'array',
'filter_mode' => [
'product_ids' => 'scalar',
'categories' => 'scalar',
'post_status' => 'scalar',
],
'campaign_parameters' => [
'utm_source' => 'scalar',
'utm_medium' => 'scalar',
'utm_campaign' => 'scalar',
'utm_term' => 'scalar',
'utm_content' => 'scalar',
],
'is_outOfStock' => 'scalar',
'is_backorder' => 'scalar',
'is_emptyDescription' => 'scalar',
'is_emptyImage' => 'scalar',
'is_emptyPrice' => 'scalar',
'product_visibility' => 'scalar',
'outofstock_visibility' => 'scalar',
'ptitle_show' => 'scalar',
'decimal_separator' => 'scalar',
'thousand_separator' => 'scalar',
'decimals' => 'scalar',
'feed_option_name' => 'scalar',
'feed_id' => 'scalar',
'meta-box-order-nonce' => 'scalar',
'shipping_country' => 'scalar',
'tax_country' => 'scalar',
'str_replace' => 'array',
'concatType' => 'array',
'fattribute' => 'array',
'condition' => 'array',
'filterCompare' => 'array',
'wf_tabs' => 'scalar',
],
'url' => 'scalar',
'last_updated' => 'scalar',
'status' => 'scalar',
],
'autoload' => 'scalar',
];
$is_valid = false;
if( $validation_type == 'partial' ) {
if(isset($feed['option_value'], $feed['option_name'],$feed['option_value']['feedrules'], $feed['option_value']['feedrules']['provider'], $feed['option_value']['feedrules']['feedType'] ) && $feed['option_value']['feedrules']['provider'] && $feed['option_value']['feedrules']['feedType'] ) {
$is_valid = true;
}
}else{
$is_valid = self::validate_structure( $feed, $required_structure );
}
return apply_filters( 'woo_feed_validate_feed_structure', $is_valid, $feed, $required_structure );
}
/**
* Validates the structure of a feed option.
*
* @param $array
* @param $required_structure
*
* @return bool
*/
private static function validate_structure( $array, $required_structure ) {
$is_valid = true;
foreach ( $required_structure as $key => $value ) {
if ( ! array_key_exists( $key, $array ) ) {
// Key is missing
$is_valid = false;
break;
}
if ( is_array( $value ) ) {
if ( ! is_array( $array[ $key ] ) ) {
// Expected an array, found something else
$is_valid = false;
break;
}
// Recursive check for nested structure
$valid = self::validate_structure( $array[ $key ], $value );
if ( ! $valid ) {
$is_valid = false;
break;
}
} else {
if ( $value === 'scalar' && is_array( $array[ $key ] ) ) {
// Expected a scalar value, found an array
$is_valid = false;
break;
}
}
}
return $is_valid;
}
/**
* Removes predefined prefixes from a feed option name and returns the resulting slug.
*
* @param string $feed The feed option name from which to remove prefixes.
*
* @return string The slug derived from the feed option name after removing specific prefixes.
*/
public static function get_feed_option_name( $feed ) {
if ( ! \is_string( $feed ) ) {
// Handle invalid input.
return '';
}
// Define the prefixes to be removed. Consider making these configurable if necessary.
$prefixes_to_remove = [ 'wf_feed_', 'wf_config' ];
return \str_replace( $prefixes_to_remove, '', $feed );
}
/**
* Get Schedule Intervals
* @return mixed
*/
public static function get_schedule_interval_options() {
if ( Helper::is_pro() ) {
$interval_options = array(
WEEK_IN_SECONDS => esc_html__( '1 Week', 'woo-feed' ),
DAY_IN_SECONDS => esc_html__( '24 Hours', 'woo-feed' ),
12 * HOUR_IN_SECONDS => esc_html__( '12 Hours', 'woo-feed' ),
6 * HOUR_IN_SECONDS => esc_html__( '6 Hours', 'woo-feed' ),
HOUR_IN_SECONDS => esc_html__( '1 Hour', 'woo-feed' ),
30 * MINUTE_IN_SECONDS => esc_html__( '30 Minutes', 'woo-feed' ),
15 * MINUTE_IN_SECONDS => esc_html__( '15 Minutes', 'woo-feed' ),
5 * MINUTE_IN_SECONDS => esc_html__( '5 Minutes', 'woo-feed' )
);
} else {
$interval_options = array(
WEEK_IN_SECONDS => esc_html__( '1 Week', 'woo-feed' ),
DAY_IN_SECONDS => esc_html__( '24 Hours', 'woo-feed' ),
12 * HOUR_IN_SECONDS => esc_html__( '12 Hours', 'woo-feed' ),
6 * HOUR_IN_SECONDS => esc_html__( '6 Hours', 'woo-feed' ),
HOUR_IN_SECONDS => esc_html__( '1 Hour', 'woo-feed' ),
);
}
return apply_filters(
'woo_feed_schedule_interval_options', $interval_options
);
}
/**
* @return false|float|int|string
*/
public static function get_minimum_interval_option() {
$intervals = \array_keys( self::get_schedule_interval_options() );
if ( ! empty( $intervals ) ) {
return \end( $intervals );
}
return 15 * MINUTE_IN_SECONDS;
}
/**
* Get Merchant list that are allowed on Custom2 Template
* @return array
*/
public static function get_custom2_merchant() {
return array( 'custom2', 'admarkt', 'yandex_xml', 'glami' );
}
/**
* Get Feed File URL
*
* @param string $file_name
* @param string $provider
* @param string $type
*
* @return string
*/
public static function get_file_url( $file_name, $provider, $type ) {
$file_name = Helper::extract_feed_option_name( $file_name );
$upload_dir = wp_get_upload_dir();
return esc_url(
\sprintf(
'%s/woo-feed/%s/%s/%s.%s',
$upload_dir['baseurl'],
$provider,
$type,
$file_name,
$type
)
);
}
/**
* Removes temporary feed files based on the given configuration and file name.
*
* @param array $config Feed configuration data.
* @param string $file_name The name of the feed file.
* @param bool $auto Flag indicating whether the process is automatic.
*
* @return void
*/
public static function unlink_temporary_files( $config, $file_name, $auto = false ) {
if ( ! \is_array( $config ) || ! \is_string( $file_name ) ) {
// Handle invalid input.
return;
}
$type = $config['feedType'];
$ext = self::get_file_type( $type );
$path = Helper::get_file_dir( $config['provider'], $type );
$temp_feed_body_prefix = self::get_feed_body_temp_prefix( $auto );
$files = [
'headerFile' => $path . '/' . AttributeValueByType::FEED_TEMP_HEADER_PREFIX . $file_name . '.' . $ext,
'bodyFile' => $path . '/' . $temp_feed_body_prefix . $file_name . '.' . $ext,
'footerFile' => $path . '/' . AttributeValueByType::FEED_TEMP_FOOTER_PREFIX . $file_name . '.' . $ext,
];
foreach ( $files as $file ) {
if ( \file_exists( $file ) ) {
\unlink( $file ); // Consider adding error handling here.
}
}
}
/**
* Removes temporary feed files based on the given configuration and file name.
*
* @param array $config Feed configuration data.
* @param string $file_name The name of the feed file.
* @param bool $auto Flag indicating whether the process is automatic.
*
* @return void
*/
public static function unlink_temporary_cron_files( $path, $option_name, $files = [] ) {
if ( empty( $files ) || ! \is_string( $option_name ) ) {
// Handle invalid input.
return;
}
foreach ( $files as $file ) {
$temp_file_name = $path . '/' . $file;
if ( \file_exists( $temp_file_name ) ) {
\unlink( $temp_file_name ); // Consider adding error handling here.
}
}
}
/**
* Saves a batch chunk of feed information to a file.
*
* @param string $feed_service Merchant service.
* @param string $type File type (extension).
* @param string|array $string Data to be saved.
* @param string $file_name File name.
* @param array $info Feed configuration.
*
* @return bool True on successful save, false otherwise.
*/
public static function save_batch_feed_info( $feed_service, $type, $string, $file_name, $info ) {
if ( ! \is_string( $feed_service ) || ! \is_string( $type ) || ! ( \is_string( $string ) || \is_array( $string ) ) || ! \is_string( $file_name ) ) {
// Handle invalid input.
return false;
}
$ext = self::get_file_type( $type );
if ( 'json' === $ext ) {
$string = \wp_json_encode( $string );
}
$path = Helper::get_file_dir( $feed_service, $type );
$file = $path . '/' . $file_name . '.' . $ext;
$status = FileSystem::saveFile( $path, $file, $string );
if ( Helper::is_debugging_enabled() ) {
$message = $status ? \sprintf( 'Batch chunk file (%s) saved.', $file_name ) :
\sprintf( 'Unable to save batch chunk file %s.', $file_name );
woo_feed_log_feed_process( $info['filename'], $message );
}
return $status;
}
/**
* Retrieves batch feed information from a file.
*
* @param string $feed_service The feed service.
* @param string $type The file type.
* @param string $file_name The file name.
*
* @return bool|array|string False if file does not exist or data is not readable, array or JSON string otherwise.
*/
public static function get_batch_feed_info( $feed_service, $type, $file_name ) {
if ( ! \is_string( $feed_service ) || ! \is_string( $type ) || ! \is_string( $file_name ) ) {
// Handle invalid input.
return false;
}
$ext = self::get_file_type( $type );
$path = Helper::get_file_dir( $feed_service, $type );
$file = $path . '/' . $file_name . '.' . $ext;
if ( ! \file_exists( $file ) ) {
return false;
}
$data = \file_get_contents( $file ); // Consider adding error handling here.
if ( false === $data ) {
return false;
}
return 'json' === $ext ? \json_decode( $data, true ) : $data;
}
/**
* Determines the appropriate file extension type for the given file type.
*
* @param string $type The file type (e.g., 'csv', 'json').
*
* @return string The determined file extension type, defaults to 'json' for certain types.
*/
public static function get_file_type( $type ) {
if ( ! \is_string( $type ) ) {
// Handle non-string type.
return '';
}
$json_types = array( 'csv', 'tsv', 'xls', 'xlsx', 'json' );
return \in_array( $type, $json_types ) ? 'json' : $type;
}
/**
* Determines if the content of a given file type should be JSON decoded.
*
* @param string $type The file type (e.g., 'csv', 'json').
*
* @return bool True if the content should be JSON decoded, false otherwise.
*/
public static function should_json_decode( $type ) {
if ( ! \is_string( $type ) ) {
// Handle non-string type.
return false;
}
$json_decodable_types = array( 'csv', 'tsv', 'xls', 'xlsx', 'json' );
return \in_array( $type, $json_decodable_types );
}
/**
* @param $file_ext_type
*
* @return bool
*/
public static function should_create_footer( $file_ext_type ) {
return 'xml' == $file_ext_type;
}
/**
* @param $value
*
* @return bool
*/
public static function is_attribute_price_type( $value ) {
return \in_array( $value, [
'price',
'current_price',
'sale_price',
'price_with_tax',
'current_price_with_tax',
'sale_price_with_tax'
] );
}
/**
* @return string[]
*/
public static function get_special_templates() {
return array(
'custom2',
'admarkt',
'glami',
'yandex_xml',
);
}
/**
* @param $feed_info
*
* @return array
*/
public static function get_product_ids( $feed_info ) {
$config = new Config( $feed_info );
do_action( 'before_woo_feed_get_product_information', $config );
$ids = QueryFactory::get_ids( $config );
do_action( 'after_woo_feed_get_product_information', $config );
return $ids;
}
private static function should_create_header_footer( $path, $feed_name, $file_type, $auto_update ) {
$should_create_header_footer = false;
$temp_feed_header_name = $auto_update ? AttributeValueByType::AUTO_FEED_TEMP_HEADER_PREFIX : AttributeValueByType::FEED_TEMP_HEADER_PREFIX;
$temp_feed_footer_name = $auto_update ? AttributeValueByType::AUTO_FEED_TEMP_FOOTER_PREFIX : AttributeValueByType::FEED_TEMP_FOOTER_PREFIX;
$temp_feed_header_name .= $feed_name . $file_type;
$temp_feed_footer_name .= $feed_name . $file_type;
if ( ! file_exists( $path . '/' . $temp_feed_header_name ) ) {
$should_create_header_footer = true;
}
if ( ! file_exists( $path . '/' . $temp_feed_footer_name ) && self::should_create_footer( $file_type ) ) {
$should_create_header_footer = true;
}
return $should_create_header_footer;
}
/**
* @param $feed_info
* @param $product_ids
* @param $offset
* @param $status
*
* @return bool|mixed
*/
public static function generate_temp_feed_body( $feed_info, $product_ids, $offset, $status = false, $auto = false ) {
$feed_rules = $feed_info['option_value']['feedrules'];
$feed_name = Helper::extract_feed_option_name( $feed_info['option_name'] );
$config = new Config( $feed_info );
do_action( 'before_woo_feed_generate_batch_data', $config );
if ( ! empty( $feed_rules['provider'] ) ) {
$provider = $config->get_feed_template();
$file_ext_type = $config->get_feed_file_type();
if ( $offset === 0 ) {
self::unlink_temporary_files( $feed_rules, $feed_rules['filename'], $auto );
}
$path = Helper::get_file_dir( $provider, $file_ext_type );
$feed_template = TemplateFactory::make_feed( $product_ids, $config );
//Generate Header footer
// TODO: call this function only when offset is 0. But when creating the new feed 0 is calling 2 times. and not generating the header footer.
if ( self::should_create_header_footer( $path, $feed_name, $file_ext_type, $auto ) ) {
self::generate_header_footer( $feed_template, $file_ext_type, $feed_name, $feed_rules, $provider, $auto );
}
$current_feed = $feed_template->get_feed();
woo_feed_log_feed_process( $feed_rules['filename'], sprintf( 'Initializing merchant Class %s for %s', $provider, $provider ) );
if ( ! empty( $current_feed ) ) {
// Get previous feed body data from temporary file to concat with current data.
//$temp_feed_body_name = AttributeValueByType::AUTO_FEED_TEMP_BODY_PREFIX . $feed_name;
$temp_feed_body_prefix = self::get_feed_body_temp_prefix( $auto );
$temp_feed_body_name = $temp_feed_body_prefix . $feed_name;
$previous_feed = self::get_batch_feed_info( $provider, $file_ext_type, $temp_feed_body_name );
// Has previous feed body.
if ( $previous_feed ) {
/**
* If file extension type is csv, tsv, xls, json, xlsx then
* merge previous array with current array
*
* Else concat previous feed body with current feed body
*/
if ( 'csv' === $file_ext_type || 'tsv' === $file_ext_type || 'xls' === $file_ext_type || 'json' === $file_ext_type || 'xlsx' === $file_ext_type ) {
if ( \is_array( $previous_feed ) ) {
$newFeed = \array_merge( $previous_feed, $current_feed );
self::save_batch_feed_info( $provider, $file_ext_type, $newFeed, $temp_feed_body_name, $feed_rules );
} else {
$newFeed = $previous_feed . $current_feed;
self::save_batch_feed_info( $provider, $file_ext_type, $newFeed, $temp_feed_body_name, $feed_rules );
}
} else {
$newFeed = $previous_feed . $current_feed;
self::save_batch_feed_info( $provider, $file_ext_type, $newFeed, $temp_feed_body_name, $feed_rules );
}
} else {
self::save_batch_feed_info( $provider, $file_ext_type, $current_feed, $temp_feed_body_name, $feed_rules );
}
$status = true;
} else {
$status = false;
}
}
do_action( 'after_woo_feed_generate_batch_data', $config );
return $status;
}
private static function get_re_indexed_files( $files ) {
$header_index = null;
$footer_index = null;
$header_value = '';
$footer_value = '';
// Find header and footer indices
foreach ( $files as $key => $value ) {
if ( strpos( $value, 'header' ) !== false ) {
$header_index = $key;
}
if ( strpos( $value, 'footer' ) !== false ) {
$footer_index = $key;
}
}
// Move header to the first position if it exists
if ( isset( $files[ $header_index ] ) && $header_index !== null ) {
$header_value = $files[ $header_index ];
unset( $files[ $header_index ] );
}
// Move footer to the last position if it exists
if ( isset( $files[ $footer_index ] ) && $footer_index !== null ) {
$footer_value = $files[ $footer_index ];
unset( $files[ $footer_index ] );
}
if ( ! empty( $header_value ) ) {
array_unshift( $files, $header_value );
}
if ( ! empty( $footer_value ) ) {
array_push( $files, $footer_value );
}
return array_values( $files );
}
/**
* @param $path
* @param $option_name
* @param $feed_type_ext
*
* @return array
*/
private static function get_re_indexed_and_valid_files( $path, $option_name, $feed_type_ext ) {
// get all batched files and merge them. and save them in one file to extension type folder.
// Read files in the directory
$files = scandir( $path );
// Filter out '.' and '..' (current and parent directory entries)
$files = array_diff( $files, array( '.', '..' ) );
$valid_files = [];
// Find header and footer indices
foreach ( $files as $key => $file ) {
if ( strpos( $file, $feed_type_ext ) === false || strpos( $file, $option_name ) === false || empty( $file ) ) {
continue;
}
if ( strpos( $file, 'header' ) !== false && strpos( $file, AttributeValueByType::AUTO_FEED_TEMP_HEADER_PREFIX ) !== false ) {
$valid_files[] = $file;
continue;
}
if ( strpos( $file, 'footer' ) !== false && strpos( $file, AttributeValueByType::AUTO_FEED_TEMP_FOOTER_PREFIX ) !== false ) {
$valid_files[] = $file;
continue;
}
if ( strpos( $file, AttributeValueByType::AUTO_FEED_TEMP_BODY_PREFIX ) === false ) {
continue;
}
$valid_files[] = $file;
}
$get_re_indexed_files = self::get_re_indexed_files( $valid_files );
return $get_re_indexed_files;
}
/**
* @param $feed_info
* @param $should_update_last_update_time
*
* <<<<<<< HEAD
* =======
*
* @return array
*/
public static function save_cron_batched_feed_files( $feed_info, $should_update_last_update_time = false, $auto = true ) {
$option_name_orginal = $feed_info['option_name'];
$provider = $feed_info['option_value']['feedrules']['provider'];
$feed_type_ext = $feed_info['option_value']['feedrules']['feedType'];
$path = Helper::get_file_dir( $provider, $feed_type_ext );
$option_name = Helper::extract_feed_option_name( $option_name_orginal );
$feed_url = Helper::get_file_url( $option_name, $provider, $feed_type_ext );
$contents = '';
$files = self::get_re_indexed_and_valid_files( $path, $option_name, $feed_type_ext );
foreach ( $files as $file ) {
$temp_content = FileSystem::ReadFile( $path, $file );
// If there is a problem regarding file system or other.
if ( is_wp_error( $temp_content ) ) {
continue;
}
if ( self::should_json_decode( $feed_type_ext ) ) {
$temp_content = \json_decode( $temp_content, true );
if ( \is_array( $temp_content ) || 'json' === $feed_type_ext ) { // json, csv fil
$temp_contents = $contents ? $contents : [];
$temp_content = $temp_content ? $temp_content : [];
$contents = \array_merge( $temp_contents, $temp_content );
} else {
$contents .= $temp_content;
}
} else {
$contents .= $temp_content;
}
}
$file_name = $option_name . '.' . $feed_type_ext;
if ( is_array( $contents ) ) { // file type json, csv
$contents = wp_json_encode( $contents );
$status = FileSystem::WriteFile( $contents, $path, $file_name );
}else if ( $contents ) {
$status = FileSystem::WriteFile( $contents, $path, $file_name );
} else {
$status = false;
}
// Upload ftp/sftp if enabled.
self::upload_feed_file_to_ftp_server( $feed_info, $path, $file_name );
// Remove temporary files.
self::unlink_temporary_cron_files( $path, $option_name, $files );
// Delete temporary cache data.
Cache::delete( 'wad_discounts' );
if ( ! isset( $feed_info['option_value']['url'] ) ) {
$feed_info['option_value']['url'] = Helper::get_file_url( $feed_info['option_name'], $feed_info['option_value']['feedrules']['provider'], $feed_info['option_value']['feedrules']['feedType'] );
}
if ( ! isset( $feed_info['option_value']['status'] ) ) {
$feed_info['option_value']['status'] = false;
}
if ( $should_update_last_update_time ) {
$feed_info['option_value']['last_updated'] = \date( 'Y-m-d H:i:s', \strtotime( \current_time( 'mysql' ) ) );
update_option( $option_name_orginal, $feed_info['option_value'] );
}
return [
'status' => $status,
'feed_url' => $feed_url,
];
}
/**
* @param $feed_info
* @param $should_update_last_update_time
*
* >>>>>>> feature/CV-79
*
* @return array
*/
public static function save_feed_file( $feed_info, $should_update_last_update_time = false, $auto = false ) {
$option_name_orginal = $feed_info['option_name'];
$provider = $feed_info['option_value']['feedrules']['provider'];
$feed_type_ext = $feed_info['option_value']['feedrules']['feedType'];
$path = Helper::get_file_dir( $provider, $feed_type_ext );
$option_name = Helper::extract_feed_option_name( $option_name_orginal );
$feed_url = Helper::get_file_url( $option_name, $provider, $feed_type_ext );
$contents = '';
$sections = [ 'header', 'body', 'footer' ];
// Remove the footer if feed type is csv
if ( ! self::should_create_footer( $feed_type_ext ) ) {
$sections = \array_filter( $sections, function ( $section ) {
return 'footer' != $section;
} );
}
$temp_file_name = '';
foreach ( $sections as $section ) {
$temp_file_ext = self::get_file_type( $feed_type_ext );
if ( 'header' === $section ) {
$temp_file_name = AttributeValueByType::FEED_TEMP_HEADER_PREFIX . $option_name . '.' . $temp_file_ext;
} elseif ( 'footer' === $section ) {
$temp_file_name = AttributeValueByType::FEED_TEMP_FOOTER_PREFIX . $option_name . '.' . $temp_file_ext;
} else {
$temp_feed_body_prefix = self::get_feed_body_temp_prefix( $auto );
$temp_file_name = $temp_feed_body_prefix . $option_name . '.' . $temp_file_ext;
}
$temp_content = FileSystem::ReadFile( $path, $temp_file_name );
// If there is a problem regarding file system or other.
if ( is_wp_error( $temp_content ) ) {
$status = new WP_Error(
$temp_content->get_error_code(),
$temp_content->get_error_message(),
[ 'status' => 404 ]
);
return [
'status' => $status,
'feed_url' => $feed_url,
];
}
if ( self::should_json_decode( $feed_type_ext ) ) {
$temp_content = \json_decode( $temp_content, true );
if ( \is_array( $temp_content ) || 'json' === $feed_type_ext ) { // json, csv fil
$temp_contents = $contents ? $contents : [];
$temp_content = $temp_content ? $temp_content : [];
$contents = \array_merge( $temp_contents, $temp_content );
} else {
$contents .= $temp_content;
}
} else {
$contents .= $temp_content;
}
}
$file_name = $option_name . '.' . $feed_type_ext;
if ( is_array( $contents ) ) { // file type json, csv
$contents = wp_json_encode( $contents );
$status = FileSystem::WriteFile( $contents, $path, $file_name );
} else {
$status = FileSystem::WriteFile( $contents, $path, $file_name );
}
// Upload ftp/sftp if enabled.
self::upload_feed_file_to_ftp_server( $feed_info, $path, $file_name );
// Remove temporary files.
self::unlink_temporary_files( $feed_info['option_value']['feedrules'], $option_name, $auto );
// Delete temporary cache data.
Cache::delete( 'wad_discounts' );
if ( ! isset( $feed_info['option_value']['url'] ) ) {
$feed_info['option_value']['url'] = Helper::get_file_url( $feed_info['option_name'], $feed_info['option_value']['feedrules']['provider'], $feed_info['option_value']['feedrules']['feedType'] );
}
if ( ! isset( $feed_info['option_value']['status'] ) ) {
$feed_info['option_value']['status'] = false;
}
if ( $should_update_last_update_time ) {
$feed_info['option_value']['last_updated'] = \date( 'Y-m-d H:i:s', \strtotime( \current_time( 'mysql' ) ) );
update_option( $option_name_orginal, $feed_info['option_value'] );
}
delete_transient( 'ctx_feed_structure_transient' );
do_action('ctx_feed_after_save_feed_file', $status, $feed_info, $should_update_last_update_time, $auto );
return [
'status' => $status,
'feed_url' => $feed_url,
];
}
/**
* @param $feed_info
* @param $path
* @param $file_name
*
* @return void
* @throws \CTXFeed\V5\FTP\FtpException
*/
private static function upload_feed_file_to_ftp_server( $feed_info, $path, $file_name ) {
/**
* class FtpClient only can upload ftp/ftps not sftp upload.
* ftp_ssl_connect method is used for FTP SSL file upload
* ssh2_sftp is intended to use for sFTP file upload
*
* That's why here we use FtpClient class for FTP file upload and
* self::handle_file_transfer for sFTP uload.
*
* @see https://secure.helpscout.net/conversation/2390941164/29741?folderId=713813
* @see https://www.php.net/manual/en/function.ftp-ssl-connect.php
* @see https://www.php.net/manual/en/function.ssh2-sftp.php
* @see https://www.spiceworks.com/tech/networking/articles/sftp-vs-ftps/
*/
$global_ftp_status= Settings:: get();
if ( isset( $global_ftp_status['enable_ftp_upload'] ) && $global_ftp_status['enable_ftp_upload']=='yes' ) {
if (isset($feed_info['option_value']['feedrules']['ftpenabled']) && $feed_info['option_value']['feedrules']['ftpenabled']) {
$path = $path . '/' . $file_name; // locale file path to upload.
if (isset($feed_info['option_value']['feedrules']['ftporsftp']) & 'ftp' === $feed_info['option_value']['feedrules']['ftporsftp']) {
/*$ftp = new FtpClient();
$ftp_connect = $ftp->connect( $feed_info['option_value']['feedrules']['ftphost'], false, $feed_info['option_value']['feedrules']['ftpport'] ); // connect to ftp/sftp server
$ftp_connect = $ftp_connect->login( $feed_info['option_value']['feedrules']['ftpuser'], $feed_info['option_value']['feedrules']['ftppassword'] ); // login to server
$ftp_connect->putFromPath( $path );*/
$remote_file = basename($path);
self::uploadFileInFtp($feed_info['option_value']['feedrules']['ftpuser'], $feed_info['option_value']['feedrules']['ftppassword'], $feed_info['option_value']['feedrules']['ftphost'], $path, $remote_file);
} else {
$feed_rules = $feed_info['option_value']['feedrules'];
$is_file_uploaded = self::handle_file_transfer($path, $file_name, $feed_rules);
if ($is_file_uploaded) {
woo_feed_log_feed_process($file_name, 'file transfer request success.');
} else {
woo_feed_log_feed_process($file_name, 'Unable to process file transfer request.');
}
}
}
}
}
/**
* Transfer file as per ftp config
*
* @param string $file_from
* @param string $file_to
* @param array $info
*
* @return bool
*/
private static function handle_file_transfer( $file_from, $file_to, $info ) {
if ( $info['ftpenabled'] ) {
if ( ! file_exists( $file_from ) ) {
\woo_feed_log_feed_process( $info['filename'], 'Unable to process file transfer request. File does not exists.' );
return false;
}
$ftp_host = \sanitize_text_field( $info['ftphost'] );
$ftp_user = \sanitize_text_field( $info['ftpuser'] );
$ftp_password = \sanitize_text_field( $info['ftppassword'] );
$ftp_path = \trailingslashit( \untrailingslashit( \sanitize_text_field( $info['ftppath'] ) ) );
$ftp_passive_mode = ( isset( $info['ftpmode'] ) && \sanitize_text_field( $info['ftpmode'] ) === 'passive' ) ? true : false;
if ( isset( $info['ftporsftp'] ) & 'ftp' === $info['ftporsftp'] ) {
$ftporsftp = 'ftp';
} else {
$ftporsftp = 'sftp';
}
if ( isset( $info['ftpport'] ) && ! empty( $info['ftpport'] ) ) {
$ftp_port = \absint( $info['ftpport'] );
} else {
$ftp_port = false;
}
if ( ! $ftp_port || ! ( ( 1 <= $ftp_port ) && ( $ftp_port <= 65535 ) ) ) {
$ftp_port = 'sftp' === $ftporsftp ? 22 : 21;
}
\woo_feed_log_feed_process( $info['filename'], sprintf( 'Uploading Feed file via %s.', $ftporsftp ) );
try {
if ( 'ftp' === $ftporsftp ) {
$ftp = new \WebAppick\FTP\FTPConnection();
if ( $ftp->connect( $ftp_host, $ftp_user, $ftp_password, $ftp_passive_mode, $ftp_port ) ) {
return $ftp->upload_file( $file_from, $ftp_path . $file_to );
}
} elseif ( 'sftp' === $ftporsftp ) {
$sftp = new \WebAppick\FTP\SFTPConnection( $ftp_host, $ftp_port );
$sftp->login( $ftp_user, $ftp_password );
return $sftp->upload_file( $file_from, $file_to, $ftp_path );
}
} catch ( \Exception $e ) {
$message = 'Error Uploading Feed Via ' . $ftporsftp . PHP_EOL . 'Caught Exception :: ' . $e->getMessage();
\woo_feed_log( $info['filename'], $message, 'critical', $e, true );
\woo_feed_log_fatal_error( $message, $e );
return false;
}
}
return false;
}
/**
* @param array $feed_rules
* @param int $offset
* @param array $product_ids
*
* @return void
*/
public static function log_data( $feed_rules, $offset, $product_ids ) {
\woo_feed_log_feed_process( $feed_rules['filename'], \sprintf( 'Processing Loop %d.', ( $offset + 1 ) ) );
$m = 'Processing Product Following Product (IDs) : ' . PHP_EOL;
foreach ( \array_chunk( $product_ids, 10 ) as $ids ) { // pretty print log [B-)=
$m .= \implode( ', ', $ids ) . PHP_EOL;
}
\woo_feed_log_feed_process( $feed_rules['filename'], $m );
}
/**
* Generates and saves the header and footer for a feed.
*
* @param object $feed_template The feed template object.
* @param string $file_ext_type The file extension type.
* @param string $feed_name The name of the feed.
* @param array $feed_rules Feed rules.
* @param string $provider The provider.
*
* @return void
*/
public static function generate_header_footer( $feed_template, $file_ext_type, $feed_name, $feed_rules, $provider, $auto_update = false ) {
$feed_header = $feed_template->get_header();
$feed_footer = $feed_template->get_footer();
$temp_feed_header_name = $auto_update ? AttributeValueByType::AUTO_FEED_TEMP_HEADER_PREFIX . $feed_name : AttributeValueByType::FEED_TEMP_HEADER_PREFIX . $feed_name;
$temp_feed_footer_name = $auto_update ? AttributeValueByType::AUTO_FEED_TEMP_FOOTER_PREFIX . $feed_name : AttributeValueByType::FEED_TEMP_FOOTER_PREFIX . $feed_name;
// TODO: should generate footer when template type is csv ?
self::save_batch_feed_info( $provider, $file_ext_type, $feed_header, $temp_feed_header_name, $feed_rules );
// create footer for xml file .
if ( self::should_create_footer( $file_ext_type ) ) {
self::save_batch_feed_info( $provider, $file_ext_type, $feed_footer, $temp_feed_footer_name, $feed_rules );
}
}
/**
* Generates a feed during a cron job.
*
* @param array $feed_info Feed configuration information.
* @param int $offset Offset for batch processing.
* @param bool $should_update_last_update_time Flag indicating whether to update the last update time.
*
* @return bool True if the feed generation is successful, false otherwise.
*/
public static function generate_feed( $feed_info, $offset = 0, $should_update_last_update_time = true ) {
if ( ! \is_array( $feed_info ) || ! isset( $feed_info['option_value'] ) ) {
// Handle invalid input.
return false;
}
if ( $offset < 0 ) {
// Handle invalid offset.
return false;
}
$ids = self::get_product_ids( $feed_info );
if ( empty( $ids ) ) {
// Handle the case where no product IDs are found.
return false;
}
/**
* This parameter is used to check if the feed is generated by cron or not.
* Even the feed is generated by cron, it will be considered as manual feed.
* Because, The header, footer and body prefix is same for the manual and old cron feed.
* But the new cron feed has different prefix for header, footer and body.
* And this function is only called from old cron job functionality. So, it's not a new cron feed.
* That's why we have to send this parameter false to generate_temp_feed_body function.
*
* @link: https://webappick.atlassian.net/browse/CBT-363
* @since 7.3.13
*/
$is_auto_feed = false;
$status = self::generate_temp_feed_body( $feed_info, $ids, $offset, false, $is_auto_feed );
if ( $status ) {
self::save_feed_file( $feed_info, $should_update_last_update_time, $is_auto_feed );
}
return $status;
}
/**
* Generates a feed during a cron job.
*
* @param array $feed_info Feed configuration information.
* @param int $offset Offset for batch processing.
* @param bool $should_update_last_update_time Flag indicating whether to update the last update time.
*
* @return bool True if the feed generation is successful, false otherwise.
*/
public static function generate_cron_batched_feed( $feed_info, $offset = 0, $should_update_last_update_time = true, $ids = [] ) {
if ( ! \is_array( $feed_info ) || ! isset( $feed_info['option_value'] ) ) {
// Handle invalid input.
return false;
}
if ( empty( $ids ) ) {
// Handle the case where no product IDs are found.
return false;
}
$status = self::generate_temp_feed_body( $feed_info, $ids, $offset, false, true );
return $status;
}
/**
* Extracts a substring from a given string, delimited by start and end markers.
*
* @param string $string The full string to extract from.
* @param string $start The start marker of the desired substring.
* @param string $end The end marker of the desired substring.
*
* @return string The extracted substring, or an empty string if markers are not found.
*/
public static function get_string_between( $string, $start, $end ) {
if ( ! \is_string( $string ) || ! \is_string( $start ) || ! \is_string( $end ) ) {
// Handle invalid inputs.
return '';
}
$start_pos = \strpos( $string, $start );
if ( $start_pos === false ) {
return '';
}
$start_pos += \strlen( $start );
$end_pos = \strpos( $string, $end, $start_pos );
if ( $end_pos === false ) {
return '';
}
return \substr( $string, $start_pos, $end_pos - $start_pos );
}
/**
* Validate old feeds during cron job.
*
* @param array $feed_info feed information.
*
* @return array An array return form old feed.
*/
public static function validate_feed( $feed_info ) {
$temp_make_feed = $feed_info;
// Modify old feed data
$temp_make_rules = $temp_make_feed['option_value']['feedrules'];
$should_modify_filter = array(
'is_outOfStock' => false,
'is_backorder' => false,
'is_emptyDescription' => false,
'is_emptyImage' => false,
'is_emptyPrice' => false,
'product_visibility' => false,
'outofstock_visibility' => false,
);
$should_modify_filter = \array_keys( $should_modify_filter );
$feed_default_value = array( 'product_ids', 'post_status', 'categories' );
foreach ( $temp_make_rules as $key => $value ) {
if ( \in_array( $key, $should_modify_filter ) ) {
$temp_make_rules[ $key ] = self::get_toggle_value( $temp_make_rules[ $key ] );
}
/**
* Some previous feed is saving an error as value of product_ids.
* this value should be an array, it's default value is an array
*/
if ( \in_array( $key, $feed_default_value ) && ! \is_array( $temp_make_rules[ $key ] ) ) {
if ( $temp_make_rules[ $key ] ) {
if ( 'product_ids' === $key ) { // if key is product_ids then remove the extra space from ids.
$temp_data = \explode( ',', $temp_make_rules[ $key ] );
$temp_data = \array_map( 'absint', $temp_data );
$temp_make_rules[ $key ] = $temp_data;
} else {
$temp_make_rules[ $key ] = \explode( ',', $temp_make_rules[ $key ] );
}
} else {
$temp_make_rules[ $key ] = array();
}
}
}
$temp_make_feed['option_value']['feedrules'] = $temp_make_rules;
return $temp_make_feed;
}
/**
* Validate feed config.
*
* @param array $feed_rules feed rules.
*
* @return array feed config.
*/
public static function validate_config( $feed_rules ) {
// Modify old feed data
$temp_feed_rules = $feed_rules;
$should_modify_filter = array(
'is_outOfStock' => false,
'is_backorder' => false,
'is_emptyDescription' => false,
'is_emptyImage' => false,
'is_emptyPrice' => false,
'product_visibility' => false,
'outofstock_visibility' => false,
'is_emptyTitle' => false,
);
$should_modify_filter = \array_keys( $should_modify_filter );
foreach ( $temp_feed_rules as $key => $value ) {
if ( \in_array( $key, $should_modify_filter ) ) {
$temp_feed_rules[ $key ] = self::get_toggle_value( $temp_feed_rules[ $key ] );
}
}
return $temp_feed_rules;
}
/**
* Determines the boolean value based on various representations of 'false'.
*
* @param mixed $toggle_value The value to evaluate.
*
* @return bool Returns false for common representations of 'false', otherwise true.
*/
public static function get_toggle_value( $toggle_value ) {
$false_values = array( 'disable', 'off', 'no', false, '', 0, 'n', '0', 'false' );
// Check if the value is in the list of 'false' representations.
return ! \in_array( $toggle_value, $false_values, true );
}
/**
* Returns the appropriate temporary feed body prefix based on the automatic flag.
*
* @param bool $auto Indicates whether the automatic mode is enabled.
*
* @return string The temporary feed body prefix.
*/
public static function get_feed_body_temp_prefix( $auto = false ) {
if ( ! \is_bool( $auto ) ) {
// Handle non-boolean input.
return AttributeValueByType::FEED_TEMP_BODY_PREFIX;
}
$temp_feed_body_prefix = $auto ? AttributeValueByType::AUTO_FEED_TEMP_BODY_PREFIX : AttributeValueByType::FEED_TEMP_BODY_PREFIX;
return apply_filters( 'woo_feed_temp_feed_body_prefix', $temp_feed_body_prefix, $auto );
}
/**
* @return bool
*/
public static function should_generate_feed_by_ajax() {
$should_generate_feed_by_ajax = true;
if ( is_plugin_active( 'polylang/polylang.php' ) ) {
$should_generate_feed_by_ajax = false;
}
if ( is_plugin_active( 'woocommerce-multilingual/wpml-woocommerce.php' ) ) {
$should_generate_feed_by_ajax = false;
}
$theme = wp_get_theme(); // gets the current theme
if ( 'Woodmart' == $theme->parent_theme ) {
$should_generate_feed_by_ajax = false;
}
return apply_filters( 'woo_feed_generate_feed_by_ajax', $should_generate_feed_by_ajax );
}
public static function uploadFileInFtp( $ftpUsername, $ftpPassword, $ftpServer, $localFilePath, $serverFilePath ) {
$conn_id = ftp_connect( $ftpServer );
if ( ! $conn_id ) {
throw new FtpException( "Failed to connect to the FTP server" );
}
$login = ftp_login( $conn_id, $ftpUsername, $ftpPassword );
if ( ! $login ) {
throw new FtpException( "FTP login failed" );
}
ftp_pasv( $conn_id, true );
if ( ! file_exists( $localFilePath ) ) {
throw new FtpException( "Local file not found: ".esc_attr($localFilePath)."." );
}
$content = file_get_contents( $localFilePath );
$tmp = fopen( tempnam( sys_get_temp_dir(), $localFilePath ), "w+" );
fwrite( $tmp, $content );
rewind( $tmp );
$upload = ftp_fput( $conn_id, $serverFilePath, $tmp, FTP_BINARY );
ftp_close( $conn_id );
if ( $upload ) {
return true;
} else {
throw new FtpException(
'Unable to put the remote file from the local file "' . esc_attr($localFilePath) . '"'
);
}
}
}