_channels = array( new Wppfm_Channel_Info( '0', 'usersetup', 'Free User Setup' ), new Wppfm_Channel_Info( '1', 'google', 'Google Merchant Centre', 'https://support.google.com/merchants/answer/188924?sjid=17852146859618914984-EU&visit_id=638328859471105636-940801196&rd=1', 'https://support.google.com/merchants/answer/7052112?hl=en' ), new Wppfm_Channel_Info( '2', 'bing', 'Bing (Microsoft) Shopping', 'https://help.ads.microsoft.com/#apex/ads/en/51105/1', 'https://help.ads.microsoft.com/apex/index/3/en/51084' ), new Wppfm_Channel_Info( '3', 'beslis', 'Beslis.nl' ), new Wppfm_Channel_Info( '4', 'pricegrabber', 'PriceGrabber' ), new Wppfm_Channel_Info( '5', 'shopping', 'Shopping.com (eBay)' ), new Wppfm_Channel_Info( '6', 'amazon', 'Amazon product ads' ), new Wppfm_Channel_Info( '7', 'connexity', 'Connexity' ), new Wppfm_Channel_Info( '8', 'become', 'Become' ), // Become has been taken over by Connexity, https://merchants.become.com/DataFeedSpecification.html links to Connexity new Wppfm_Channel_Info( '9', 'nextag', 'Nextag' ), new Wppfm_Channel_Info( '10', 'kieskeurig', 'Kieskeurig.nl' ), new Wppfm_Channel_Info( '11', 'vergelijk', 'Vergelijk.nl' ), new Wppfm_Channel_Info( '12', 'koopjespakker', 'Koopjespakker.nl' ), new Wppfm_Channel_Info( '13', 'avantlink', 'AvantLink' ), new Wppfm_Channel_Info( '14', 'zbozi', 'Zbozi', 'https://napoveda.zbozi.cz/en/starting-to-advertise/', 'https://napoveda.zbozi.cz/en/xml-feed-2/specification/' ), new Wppfm_Channel_Info( '15', 'comcon', 'Commerce Connector' ), new Wppfm_Channel_Info( '16', 'facebook', 'Facebook', 'https://business.facebook.com/commerce_manager/get_started/', 'https://www.facebook.com/business/help/120325381656392?id=725943027795860' ), new Wppfm_Channel_Info( '17', 'bol', 'Bol.com' ), new Wppfm_Channel_Info( '18', 'adtraction', 'Adtraction', 'https://adtraction.com/advertisers', 'https://www.wpmarketingrobot.com/system/wp-content/uploads/2023/10/Adtraction_datafeedspecification.pdf' ), new Wppfm_Channel_Info( '19', 'ricardo', 'Ricardo.ch' ), new Wppfm_Channel_Info( '20', 'ebay', 'eBay' ), new Wppfm_Channel_Info( '21', 'shopzilla', 'Shopzilla' ), new Wppfm_Channel_Info( '22', 'converto', 'Converto' ), new Wppfm_Channel_Info( '23', 'idealo', 'Idealo' ), new Wppfm_Channel_Info( '24', 'heureka', 'Heureka', 'https://heureka.group/cz-en/for-brands/', 'https://www.heurekashopping.com/resources/attachments/p0/40/xml-filespecification.pdf' ), new Wppfm_Channel_Info( '25', 'pepperjam', 'Pepperjam' ), new Wppfm_Channel_Info( '26', 'galaxus_data', 'Galaxus Product Data' ), new Wppfm_Channel_Info( '27', 'galaxus_properties', 'Galaxus Product Properties' ), new Wppfm_Channel_Info( '28', 'galaxus_stock_pricing', 'Galaxus Product Stock Pricing' ), new Wppfm_Channel_Info( '29', 'vivino', 'Vivino' ), new Wppfm_Channel_Info( '30', 'snapchat', 'Snapchat Product Catalog', 'https://businesshelp.snapchat.com/s/topic/0TO0y000000YVd8GAG/catalogs?language=en_US', 'https://businesshelp.snapchat.com/s/article/product-catalog-specs?language=en_US' ), new Wppfm_Channel_Info( '31', 'pinterest', 'Pinterest', 'https://help.pinterest.com/en/business/article/before-you-get-started-with-catalogs', '' ), new Wppfm_Channel_Info( '32', 'vivino_xml', 'Vivino XML', 'https://help.vivino.com/s/article/How-does-Vivino-pull-my-inventory?language=en_US', 'https://vivino.slab.com/public/posts/vivino-feed-creation-guidelines-9gq0o3dg' ), new Wppfm_Channel_Info( '33', 'idealo_xml', 'Idealo XML', 'https://partner.idealo.com/uk/learning-center/faq-technical-integration', 'https://idealo.github.io/csv-importer/en/csv/' ), new Wppfm_Channel_Info( '34', 'x_shopping_manager', 'Twitter Shopping', 'https://business.twitter.com/en/products/shopping/shopping-manager.html', 'https://business.twitter.com/en/help/shopping-specs.html' ), new Wppfm_Channel_Info( '35', 'instagram_shopping', 'Instagram Shopping', 'https://business.instagram.com/shopping/?content_id=IE6I4Ax2NPXa0CM', 'https://www.facebook.com/business/help/161324715892485' ), new Wppfm_Channel_Info( '36', 'whatsapp_business', 'WhatsApp Business', 'https://business.whatsapp.com/products/business-app-features', 'https://www.facebook.com/business/help/161324715892485' ), new Wppfm_Channel_Info( '37', 'tiktok_catalog', 'TikTok Catalog', 'https://getstarted.tiktok.com/gofulltiktok?lang=en', 'https://ads.tiktok.com/help/article/catalog-product-parameters?lang=en' ), new Wppfm_Channel_Info( '38', 'atalanda', 'Atalanda', 'https://atalanda.com/mitmachen', 'https://atalanda.com/dev#google_xml' ), new Wppfm_Channel_Info( '39', 'reddit', 'Reddit Catalog', 'https://business.reddithelp.com/helpcenter/articles/Knowledge/catalogs', 'https://business.reddithelp.com/helpcenter/s/article/catalog-requirements' ), new Wppfm_Channel_Info( '40', 'chatgpt', 'ChatGpt', 'https://developers.openai.com/commerce', 'https://developers.openai.com/commerce/specs/feed' ), new Wppfm_Channel_Info( '996', 'marketingrobot_tsv', 'Custom TSV Export' ), new Wppfm_Channel_Info( '997', 'marketingrobot_txt', 'Custom TXT Export' ), new Wppfm_Channel_Info( '998', 'marketingrobot_csv', 'Custom CSV Export' ), new Wppfm_Channel_Info( '999', 'marketingrobot', 'Custom XML Export' ), ); } /** * Returns channel data from a specific channel * * @param string $channel_name channel name * * @return string Channel class */ public function get_active_channel_details( $channel_name ) { foreach ( $this->_channels as $channel ) { if ( $channel->channel_short === $channel_name ) { return $channel; } } return false; } /** * Returns a channel short name from a specific channel, or false if the name was not found. * * @param string $channel_id channel id * * @return string|bool Channel class */ public function get_channel_short_name( $channel_id ) { foreach ( $this->_channels as $channel ) { if ( $channel->channel_id === $channel_id ) { return sanitize_file_name( (string) $channel->channel_short ); } } return false; } /** * Returns a channel name from a specific channel, or false if the name was not found. * * @param string $channel_id channel id * * @return string|bool Channel class */ public function get_channel_name( $channel_id ) { foreach ( $this->_channels as $channel ) { if ( $channel->channel_id === $channel_id ) { return (string) $channel->channel_name; } } return false; } /** * Returns the installed channel names. * * @return array The installed channel names. */ public function get_installed_channel_names() { $file_class = new WPPFM_File(); return $file_class->get_installed_channels_from_file(); } /** * Returns the channel info link. * * @param string $channel_short_name The channel short name. * * @return bool The channel info link. */ public function get_channel_info_link( $channel_short_name ) { foreach ( $this->_channels as $channel ) { if ( $channel->channel_short === $channel_short_name ) { return $channel->channel_info_link; } } return false; } /** * Returns the channel specifications link. * * @param string $channel_short_name The channel short name. * * @return bool The channel specifications link. */ public function get_channel_specifications_link( $channel_short_name ) { foreach ( $this->_channels as $channel ) { if ( $channel->channel_short === $channel_short_name ) { return $channel->channel_specifications_link; } } return false; } /** * Removes a channel from the server. * * @param string $channel The channel to remove. * @param string $nonce The nonce. */ public function remove_channel( $channel, $nonce ) { if ( wp_verify_nonce( $nonce, 'delete-channel-nonce' ) ) { $this->remove_channel_source( $channel ); } } /** * Updates a channel from the server. * * @param string $channel_short_name The channel short name. * @param string $code The channel code. * @param string $nonce The nonce. */ public function update_channel( $channel_short_name, $code, $nonce ) { if ( wp_verify_nonce( $nonce, 'update-channel-nonce' ) ) { $this->update_channel_source( $channel_short_name, $code ); } else { wppfm_write_log_file( sprintf( 'Failed to update channel %s because then nonce was not accepted. Given nonce = %s', $channel_short_name, $nonce ) ); } } /** * Installs a channel from the server. * * @param string $channel The channel to install. * @param string $code The channel code. * @param string $nonce The nonce. */ public function install_channel( $channel, $code, $nonce ) { if ( wp_verify_nonce( $nonce, 'install-channel-nonce' ) ) { $this->install_channel_source( $channel, $code ); } else { wppfm_write_log_file( sprintf( 'Failed to install channel %s because then nonce was not accepted. Given nonce = %s', $channel, $nonce ) ); } } /** * Returns the channels from the server * * @return array|WP_Error The channels from the server */ public function get_channels_from_server() { $url = trailingslashit( WPPFM_EDD_SL_STORE_URL ) . 'wpmr/channels/channels.php'; $response = wp_remote_post( $url, array( 'body' => array( 'unique-site-id' => trim( get_option( 'wppfm_lic_key' ) ), 'item_name' => WPPFM_EDD_SL_ITEM_NAME, ), ) ); // @since 2.3.0 // @since 3.11.0 - Added a wp_remote_retrieve_response_code check. if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { wppfm_handle_wp_errors_response( $response, sprintf( /* translators: %s: url to the wp marketingrobot server */ __( '2832 - Failed to connect with the wp marketingrobot server. Please wait a few minutes and try again. If the issue persists, open a support ticket at %s.', 'wp-product-feed-manager' ), WPPFM_SUPPORT_PAGE_URL ) ); } return $response; } /** * Returns the number of channel updates available from the server * * @param bool $channel_updated Flag indicating if a channel has been updated * * @return int|false The number of updates available, or false on failure */ public function get_number_of_channel_updates_from_server( $channel_updated ) { //@since 3.7.0. Only display channel updates available if the user chooses to update the channels manually if ( 'true' === get_option( 'wppfm_manual_channel_update', 'false' ) ) { return 0; } if ( gmdate( 'Ymd' ) === get_option( 'wppfm_channel_update_check_date' ) ) { // Only check once a day if ( $channel_updated ) { wppfm_decrease_update_ready_channels(); } return get_option( 'wppfm_channels_to_update' ); } else { $response = $this->get_channels_from_server(); if ( ! is_wp_error( $response ) ) { $available_channels = json_decode( $response['body'] ); if ( $available_channels ) { $installed_channels_names = $this->get_installed_channel_names(); $this->add_status_data_to_available_channels( $available_channels, $installed_channels_names, false ); $stored_count = $this->count_updatable_channels( $available_channels ); $count = $channel_updated ? ( $stored_count - 1 ) : $stored_count; update_option( 'wppfm_channels_to_update', max( $count, 0 ) ); update_option( 'wppfm_channel_update_check_date', gmdate( 'Ymd' ) ); return $count; } } else { wppfm_handle_wp_errors_response( $response, sprintf( /* translators: %s: url to the support page */ __( '2141 - Your WooCommerce Product Feed Manager plugin is unable to check for channel updates. This could be due to a temporary problem with our server. If this message does not clear in 15 minutes, please open a support ticket at %s for support on this issue.', 'wp-product-feed-manager' ), WPPFM_SUPPORT_PAGE_URL ) ); return false; } } return 0; } /** * Adds status data to the available channels * * @param array $available_channels The available channels * @param array $installed_channels The installed channels * @param bool $updated The updated flag */ public function add_status_data_to_available_channels( $available_channels, $installed_channels, $updated ) { for ( $i = 0; $i < count( $available_channels ); $i ++ ) { if ( in_array( $available_channels[ $i ]->short_name, $installed_channels, true ) ) { $available_channels[ $i ]->status = 'installed'; $available_channels[ $i ]->installed_version = $available_channels[ $i ]->short_name === $updated ? $available_channels[ $i ]->version : $this->get_channel_file_version( $available_channels[ $i ]->short_name, 0 ); } else { $available_channels[ $i ]->status = 'not installed'; $available_channels[ $i ]->installed_version = '0'; } } } public function add_channel_info_links_to_channels( $available_channels ) { for ( $i = 0; $i < count( $available_channels ); $i ++ ) { $available_channels[ $i ]->info_link = $this->get_channel_info_link( $available_channels[ $i ]->short_name ); $available_channels[ $i ]->specifications_link = $this->get_channel_specifications_link( $available_channels[ $i ]->short_name ); } } /** * Returns the name of the channel for a specific feed. * * @param string $feed_id The feed id. * * @since 2.20.0 * @return string The name of the channel */ public function get_channel_name_from_feed_id( $feed_id ) { $queries_class = new WPPFM_Queries(); $feed_data = $queries_class->get_feed_row( $feed_id ); return $this->get_channel_name( $feed_data->channel_id ); } /** * Returns the short name of the channel for a specific feed. * * @param string $feed_id The feed id. * * @since 2.20.0 * @return string The short name of the channel */ public function get_channel_short_name_from_feed_id( $feed_id ) { $queries_class = new WPPFM_Queries(); $feed_data = $queries_class->get_feed_row( $feed_id ); return $this->get_channel_short_name( $feed_data->channel_id ); } /** * Returns the version of the channel file. * * @param string $channel_name The channel name. * @param int $rerun_counter The rerun counter. * @param bool $silent The silent flag. * * @return string The version of the channel file */ public function get_channel_file_version( $channel_name, $rerun_counter, $silent = false ) { if ( $rerun_counter < 3 ) { if ( class_exists( 'WPPFM_' . ucfirst( $channel_name ) . '_Feed_Class' ) ) { $class_var = 'WPPFM_' . ucfirst( $channel_name ) . '_Feed_Class'; $channel_class = new $class_var(); return $channel_class->get_version(); } else { // reset the registered channels in the channel table $db_class = new WPPFM_Database_Management(); $db_class->reset_channel_registration(); include_channels(); // include the channel classes $rerun_counter ++; return $this->get_channel_file_version( $channel_name, $rerun_counter, $silent ); } } else { if ( wppfm_on_any_own_plugin_page() && ! $silent ) { /* translators: %s: Name of a channel */ wppfm_show_wp_error( sprintf( __( 'Channel %s is not installed correctly. Please try to Deactivate and then Activate the Feed Manager Plugin in your Plugins page.', 'wp-product-feed-manager' ), $channel_name ) ); wppfm_write_log_file( sprintf( 'Error: Channel %s is not installed correctly.', $channel_name ) ); } return 'unknown'; } } private function count_updatable_channels( $channel_data ) { $counter = 0; foreach ( $channel_data as $channel ) { if ( 'installed' === $channel->status && ( $channel->version > $channel->installed_version ) ) { $counter ++; } } return $counter; } /** * Updates a channel on the server. * * @param string $channel_short_name The channel short name. * @param string $code The channel code. */ private function update_channel_source( $channel_short_name, $code ) { $file_class = new WPPFM_File(); $ftp_class = new WPPFM_Channel_FTP(); // remove the outdated channel source files from the server $file_class->delete_channel_source_files( $channel_short_name ); $get_result = $ftp_class->get_channel_source_files( $channel_short_name, $code ); // get the update files from wp marketingrobot.com if ( false !== $get_result ) { // unzip the file $file_class->unzip_channel_file( $channel_short_name ); // register the update wppfm_decrease_update_ready_channels(); } } /** * Removes a channel from the server. * * @param string $channel_short The channel short name. */ private function remove_channel_source( $channel_short ) { $data_class = new WPPFM_Data(); $file_class = new WPPFM_File(); // get the channel id that needs to be removed $channel_id = $data_class->get_channel_id_from_short_name( $channel_short ); // unregister the channel wp_dequeue_script( 'wppfm_' . $channel_short . '-source-script' ); if ( $channel_id ) { // remove channel related feed files $file_class->delete_channel_feed_files( $channel_id ); // remove any channel related feed data and feed meta $data_class->delete_channel_feeds( $channel_id ); } // remove the channel from the feedmanager_channel table $data_class->delete_channel( $channel_short ); // remove the channel source files from the server $file_class->delete_channel_source_files( $channel_short ); } /** * Installs a channel from the server. * * @param string $channel_name The channel name. * @param string $code The channel code. */ private function install_channel_source( $channel_name, $code ) { $ftp_class = new WPPFM_Channel_FTP(); $file_class = new WPPFM_File(); $data_class = new WPPFM_Data(); if ( wppfm_plugin_version_supports_channel( $channel_name ) ) { // Get the update files from wp marketingrobot.com. $get_result = $ftp_class->get_channel_source_files( $channel_name, $code ); if ( false !== $get_result ) { $ftp_class->register_channel_download( $channel_name ); // @since 3.13.0. // Unzip the file. $file_class->unzip_channel_file( $channel_name ); // Register the new channel. $channel_details = $this->get_active_channel_details( $channel_name ); if ( false !== $channel_details ) { $data_class->register_channel( $channel_name, $channel_details ); } else { wppfm_write_log_file( sprintf( 'Unable to register channel %s', $channel_name ) ); } } else { wppfm_write_log_file( sprintf( 'Could not get the %s channel file from the server. Get_result message is %s.', $channel_name, false ) ); } } else { wppfm_show_wp_warning( sprintf( /* translators: %s: Name of the selected channel */ __( 'Channel %s is not supported by your current plugin version. Please update your plugin to the latest version and try uploading this channel again.', 'wp-product-feed-manager' ), $channel_name ), ); } } } // end of WPPFM_Channel class class Wppfm_Channel_Info { public $channel_id; public $channel_short; public $channel_name; public $channel_info_link; public $channel_specifications_link; public function __construct( $id, $short, $name, $info_link = '', $specifications_link = '' ) { $this->channel_id = $id; $this->channel_short = $short; $this->channel_name = $name; $this->channel_info_link = $info_link; $this->channel_specifications_link = $specifications_link; } } // end of Wppfm_Channel_Info class endif;