Changeset 3475673
- Timestamp:
- 03/05/2026 01:17:41 PM (4 weeks ago)
- Location:
- api2cart-bridge-connector/trunk
- Files:
-
- 3 edited
-
api2cart-bridge-connector.php (modified) (1 diff)
-
bridge2cart/bridge.php (modified) (51 diffs)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
api2cart-bridge-connector/trunk/api2cart-bridge-connector.php
r3439288 r3475673 5 5 Author: API2Cart 6 6 Author URI: https://api2cart.com/ 7 Version: 3.0.1 07 Version: 3.0.11 8 8 */ 9 9 -
api2cart-bridge-connector/trunk/bridge2cart/bridge.php
r3439288 r3475673 441 441 public function getAdapterPath( $cartType ) { 442 442 return A2CBC_STORE_BASE_DIR . A2CBC_BRIDGE_DIRECTORY_NAME . DIRECTORY_SEPARATOR . 'app' . 443 DIRECTORY_SEPARATOR . 'class' . DIRECTORY_SEPARATOR . 'config_adapter' . DIRECTORY_SEPARATOR . $cartType . '.php';443 DIRECTORY_SEPARATOR . 'class' . DIRECTORY_SEPARATOR . 'config_adapter' . DIRECTORY_SEPARATOR . $cartType . '.php'; 444 444 } 445 445 … … 766 766 767 767 private $_wpmlEnabled = false; 768 769 private $_polylangEnabled = false; 768 770 769 771 /** … … 929 931 930 932 if ( file_exists( $pluginsDir . DIRECTORY_SEPARATOR . 'shopp' . DIRECTORY_SEPARATOR . 'Shopp.php' ) 931 || file_exists( $pluginsDir . DIRECTORY_SEPARATOR . 'wp-e-commerce' . DIRECTORY_SEPARATOR . 'editor.php' )933 || file_exists( $pluginsDir . DIRECTORY_SEPARATOR . 'wp-e-commerce' . DIRECTORY_SEPARATOR . 'editor.php' ) 932 934 ) { 933 935 $this->imagesDir = wp_upload_dir( null, false )['basedir'] . DIRECTORY_SEPARATOR . 'wpsc' . DIRECTORY_SEPARATOR; … … 999 1001 } 1000 1002 1003 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) ) { 1004 $this->_wpmlEnabled = true; 1005 } 1006 1007 $pll = null; 1008 1009 if ( function_exists( 'PLL' ) ) { 1010 $pll = PLL(); 1011 } 1012 1013 if ( $pll ) { 1014 $this->_polylangEnabled = true; 1015 } elseif ( defined( 'POLYLANG_VERSION' ) ) { 1016 $this->_polylangEnabled = true; 1017 } 1018 1001 1019 $this->cartVars['wp_content_url'] = content_url(); 1002 1020 … … 1017 1035 1018 1036 return true; 1037 } 1038 1039 /** 1040 * @param int|null $storeId Store ID 1041 * 1042 * @return void 1043 */ 1044 private function _initWooCommerceInMultiSite( $storeId = null ) 1045 { 1046 $wooLoaded = false; 1047 1048 if ( class_exists( 'WC_Product' ) || class_exists( 'WooCommerce' ) ) { 1049 $wooLoaded = true; 1050 } 1051 1052 $wooPluginRel = 'woocommerce/woocommerce.php'; 1053 $activePlugins = (array)get_option( 'active_plugins', [] ); 1054 1055 if ( $storeId !== null && function_exists( 'get_blog_option' ) && function_exists( 'is_multisite' ) && is_multisite() ) { 1056 $activePlugins = (array)get_blog_option( (int)$storeId, 'active_plugins', [] ); 1057 } 1058 1059 $isActive = in_array( $wooPluginRel, $activePlugins, true ); 1060 $sitewide = []; 1061 1062 if ( function_exists( 'is_multisite' ) && is_multisite() ) { 1063 $sitewide = (array)get_site_option( 'active_sitewide_plugins', [] ); 1064 } 1065 1066 $shouldLoadWoo = true; 1067 1068 if ( $wooLoaded ) { 1069 $shouldLoadWoo = false; 1070 } 1071 1072 if ( $shouldLoadWoo ) { 1073 if ( $isActive || isset( $sitewide[$wooPluginRel] ) ) { 1074 $pluginPath = WP_PLUGIN_DIR . '/' . $wooPluginRel; 1075 1076 if ( is_readable( $pluginPath ) ) { 1077 include_once $pluginPath; 1078 } 1079 1080 if ( function_exists( 'WC' ) ) { 1081 WC(); 1082 1083 if ( !function_exists( 'did_action' ) || 0 === did_action( 'woocommerce_init' ) ) { 1084 WC()->init(); 1085 } 1086 } 1087 1088 if ( function_exists( 'taxonomy_exists' ) && class_exists( 'WC_Post_Types' ) 1089 && !( taxonomy_exists( 'product_cat' ) && taxonomy_exists( 'product_tag' ) ) 1090 ) { 1091 WC_Post_Types::register_taxonomies(); 1092 } 1093 1094 if ( function_exists( 'post_type_exists' ) && class_exists( 'WC_Post_Types' ) && !post_type_exists( 'product' ) ) { 1095 WC_Post_Types::register_post_types(); 1096 } 1097 } 1098 } 1099 1100 $wpmlPluginRel = 'sitepress-multilingual-cms/sitepress.php'; 1101 $wpmlActive = in_array( $wpmlPluginRel, $activePlugins, true ); 1102 1103 if ( $wpmlActive || isset( $sitewide[$wpmlPluginRel] ) ) { 1104 $wpmlPath = WP_PLUGIN_DIR . '/' . $wpmlPluginRel; 1105 1106 if ( is_readable( $wpmlPath ) ) { 1107 include_once $wpmlPath; 1108 } 1109 } 1110 1111 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) 1112 && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) 1113 ) { 1114 $this->_wpmlEnabled = true; 1115 1116 $hasWpmlDic = false; 1117 1118 if (isset($GLOBALS['wpml_dic'])) { 1119 $hasWpmlDic = true; 1120 } 1121 1122 if ($hasWpmlDic === false) { 1123 $wpmlBootstrap = WP_PLUGIN_DIR . '/sitepress-multilingual-cms/vendor/wpml/wpml/wpml.php'; 1124 1125 if (is_readable($wpmlBootstrap)) { 1126 include_once $wpmlBootstrap; 1127 } 1128 } 1129 1130 1131 return; 1132 } 1133 1134 $polylangPluginRel = 'polylang/polylang.php'; 1135 $polylangWcPluginRel = 'polylang-wc/polylang-wc.php'; 1136 $polylangActive = in_array( $polylangPluginRel, $activePlugins, true ); 1137 $polylangWcActive = in_array( $polylangWcPluginRel, $activePlugins, true ); 1138 1139 if ( $polylangActive || isset( $sitewide[$polylangPluginRel] ) ) { 1140 $polylangPath = WP_PLUGIN_DIR . '/' . $polylangPluginRel; 1141 1142 if ( is_readable( $polylangPath ) ) { 1143 include_once $polylangPath; 1144 } 1145 1146 if ( $polylangWcActive || isset( $sitewide[$polylangWcPluginRel] ) ) { 1147 $polylangWcPath = WP_PLUGIN_DIR . '/' . $polylangWcPluginRel; 1148 1149 if ( is_readable( $polylangWcPath ) ) { 1150 include_once $polylangWcPath; 1151 } 1152 } 1153 } 1154 1155 if ( class_exists( 'Polylang' ) && !isset( $GLOBALS['polylang'] ) ) { 1156 $polylangBootstrap = new Polylang(); 1157 $GLOBALS['polylang'] = $polylangBootstrap; 1158 1159 if ( method_exists( $polylangBootstrap, 'init' ) ) { 1160 $polylangBootstrap->init(); 1161 } 1162 } 1163 1164 if ( function_exists( 'PLL' ) || defined( 'POLYLANG_VERSION' ) ) { 1165 $this->_polylangEnabled = true; 1166 } 1167 1168 if ( $this->_polylangEnabled ) { 1169 $pll = null; 1170 $model = null; 1171 1172 if ( function_exists( 'PLL' ) ) { 1173 $pll = PLL(); 1174 } 1175 1176 if ( $pll && isset( $pll->model ) ) { 1177 $model = $pll->model; 1178 } 1179 1180 $this->_initPolylangWc( $model ); 1181 } 1182 } 1183 1184 /** 1185 * @param int $storeId Store ID 1186 * 1187 * @return void 1188 */ 1189 private function _initWooCommerceInMultiSiteContext( $storeId ) 1190 { 1191 if ( function_exists( 'switch_to_blog' ) ) { 1192 switch_to_blog( $storeId ); 1193 } 1194 1195 if ( function_exists( 'is_multisite' ) && is_multisite() ) { 1196 $mainBlogId = 1; 1197 1198 if ( function_exists( 'get_main_site_id' ) ) { 1199 $mainBlogId = (int)get_main_site_id(); 1200 } 1201 1202 if ( (int)$storeId !== $mainBlogId ) { 1203 $this->_initWooCommerceInMultiSite( $storeId ); 1204 } 1205 } 1206 } 1207 1208 /** 1209 * Ensure Polylang-WC taxonomies and cache are initialized 1210 * 1211 * @param object|null $model Polylang model 1212 * 1213 * @return void 1214 */ 1215 protected function _initPolylangWc($model = null) 1216 { 1217 $pllWC = null; 1218 $languages = array(); 1219 $languagesReady = false; 1220 1221 if ( function_exists( 'PLLWC' ) ) { 1222 $pllWC = PLLWC(); 1223 } 1224 1225 if ( function_exists( 'pll_languages_list' ) ) { 1226 $languages = pll_languages_list(); 1227 } 1228 1229 if ( is_array( $languages ) && $languages ) { 1230 $languagesReady = true; 1231 } 1232 1233 if ( $pllWC && $languagesReady && method_exists( $pllWC, 'init' ) ) { 1234 $pllWC->init(); 1235 } 1236 1237 if ( $model && $languagesReady && isset( $model->cache ) 1238 && is_object( $model->cache ) && method_exists( $model->cache, 'clean' ) 1239 ) { 1240 $model->cache->clean( 'taxonomies' ); 1241 1242 if ( method_exists( $model, 'get_translated_taxonomies' ) ) { 1243 $model->get_translated_taxonomies( false ); 1244 } 1245 } 1019 1246 } 1020 1247 … … 1116 1343 1117 1344 if ( empty($data) ) { 1118 $entity-> update_meta_data( '_wc_shipment_tracking_items', []);1345 $entity->delete_meta_data( '_wc_shipment_tracking_items' ); 1119 1346 } else { 1120 1347 $entity->update_meta_data( '_wc_shipment_tracking_items', $data ); … … 1135 1362 // translators: %1$s is the tracking provider, %2$s is the tracking number 1136 1363 $note = sprintf( 1137 esc_html__( 'Tracking info was deleted for tracking provider %1$s with tracking number %2$s', ' api2cart-bridge-connector' ),1364 esc_html__( 'Tracking info was deleted for tracking provider %1$s with tracking number %2$s', 'bridge-connector' ), 1138 1365 $trackingProvider, 1139 1366 $trackingNumber ); … … 1406 1633 $keys = implode( '\', \'', $wpdb->_escape( array_keys( $a2cData['meta'] ) ) ); 1407 1634 1408 switch ( $a2cData['entity'] ) {1409 case 'product':1410 case 'order':1411 $wooOrderTableEnabled = 'order' === $a2cData['entity'] && get_option('woocommerce_custom_orders_table_enabled') === 'yes';1412 1413 if ($wooOrderTableEnabled) {1414 $metaTable = $wpdb->prefix . 'wc_orders_meta';1415 $metaIdField = 'm.id AS meta_id';1416 $entityIdField = 'm.order_id';1417 } else {1418 $metaTable = $wpdb->postmeta;1419 $metaIdField = 'm.meta_id';1420 $entityIdField = 'm.post_id';1421 }1422 1423 $sql = "1635 switch ( $a2cData['entity'] ) { 1636 case 'product': 1637 case 'order': 1638 $wooOrderTableEnabled = 'order' === $a2cData['entity'] && get_option('woocommerce_custom_orders_table_enabled') === 'yes'; 1639 1640 if ($wooOrderTableEnabled) { 1641 $metaTable = $wpdb->prefix . 'wc_orders_meta'; 1642 $metaIdField = 'm.id AS meta_id'; 1643 $entityIdField = 'm.order_id'; 1644 } else { 1645 $metaTable = $wpdb->postmeta; 1646 $metaIdField = 'm.meta_id'; 1647 $entityIdField = 'm.post_id'; 1648 } 1649 1650 $sql = " 1424 1651 SELECT {$metaIdField}, m.meta_key, m.meta_value 1425 1652 FROM {$metaTable} AS m 1426 1653 WHERE {$entityIdField} = %d 1427 1654 AND m.meta_key IN ('{$keys}')"; 1428 $qRes = $wpdb->get_results($wpdb->prepare( $sql, $id ));1429 break;1430 1431 case 'customer':1655 $qRes = $wpdb->get_results($wpdb->prepare( $sql, $id )); 1656 break; 1657 1658 case 'customer': 1432 1659 $qRes = $wpdb->get_results( 1433 1660 $wpdb->prepare( ' … … 1603 1830 if ( empty( $a2cData['from'] ) ) { 1604 1831 /* translators: %s: new order status */ 1605 $transition_note = sprintf( esc_html__( 'Order status set to %s.', ' api2cart-bridge-connector' ), wc_get_order_status_name( $a2cData['to'] ) );1832 $transition_note = sprintf( esc_html__( 'Order status set to %s.', 'bridge-connector' ), wc_get_order_status_name( $a2cData['to'] ) ); 1606 1833 1607 1834 if ( empty( $a2cData['added_by_user'] ) ) { … … 1612 1839 } else { 1613 1840 /* translators: 1: old order status 2: new order status */ 1614 $transition_note = sprintf( esc_html__( 'Order status changed from %1$s to %2$s.', ' api2cart-bridge-connector' ),1841 $transition_note = sprintf( esc_html__( 'Order status changed from %1$s to %2$s.', 'bridge-connector' ), 1615 1842 wc_get_order_status_name( $a2cData['from'] ), 1616 1843 wc_get_order_status_name( $a2cData['to'] ) ); … … 1744 1971 $entity = WC()->order_factory->get_order( $a2cData['order']['id'] ); 1745 1972 1746 if (!empty( $a2cData[ 'order']['meta_data'] ) ) {1747 foreach ( $a2cData['order']['meta_data'] as $metaData) {1748 $entity->update_meta_data( $metaData['key'], $metaData['value']);1973 if (!empty( $a2cData["order"]["meta_data"] ) ) { 1974 foreach ( $a2cData["order"]["meta_data"] as $metaData ) { 1975 $entity->update_meta_data( $metaData['key'], $metaData['value'] ); 1749 1976 } 1750 1977 } … … 1819 2046 1820 2047 try { 2048 $this->_initWooCommerceInMultiSiteContext( $a2cData['store_id'] ); 2049 1821 2050 $termType = 'product_cat'; 1822 1823 if ( function_exists( 'switch_to_blog' ) ) {1824 switch_to_blog( $a2cData['store_id'] );1825 }1826 2051 1827 2052 $termArgs = [ … … 1897 2122 do_action( 'wpml_set_element_language_details', $setLanguageArgs ); 1898 2123 } else { 1899 throw new Exception( esc_html__( '[BRIDGE ERROR]: Can\'t create category!', 'api2cart-bridge-connector' ) ); 2124 throw new Exception( esc_html__( '[BRIDGE ERROR]: Can\'t create category!', 'bridge-connector' ) ); 2125 } 2126 } elseif ( $this->_polylangEnabled ) { 2127 $polylangLangCode = isset( $a2cData['meta_data']['lang_code'] ) ? $a2cData['meta_data']['lang_code'] : null; 2128 2129 if ( !$polylangLangCode && function_exists( 'pll_default_language' ) ) { 2130 $polylangLangCode = pll_default_language( 'slug' ); 2131 } 2132 2133 $polylangModel = $this->_getPolylangModel(); 2134 $polylangTermModel = $polylangModel && isset( $polylangModel->term ) ? $polylangModel->term : null; 2135 2136 if ( $polylangLangCode && !empty( $termArgs['parent'] ) ) { 2137 $translatedParentId = $this->_createPolylangTermTranslation( 2138 (int)$termArgs['parent'], 2139 $polylangLangCode, 2140 $termType, 2141 $polylangTermModel 2142 ); 2143 2144 if ( $translatedParentId ) { 2145 $termArgs['parent'] = $translatedParentId; 2146 } 2147 } 2148 2149 $newTerm = wp_insert_term( $a2cData['meta_data']['tag-name'], $termType, $termArgs ); 2150 2151 if ( $newTerm && !is_wp_error( $newTerm ) ) { 2152 $termId = 0; 2153 2154 if ( $newTerm instanceof WP_Term ) { 2155 $termId = (int)$newTerm->term_id; 2156 } elseif ( is_array( $newTerm ) && isset( $newTerm['term_id'] ) ) { 2157 $termId = (int)$newTerm['term_id']; 2158 } 2159 2160 if ( $termId > 0 ) { 2161 if ( function_exists( 'pll_set_term_language' ) ) { 2162 pll_set_term_language( $termId, $polylangLangCode ); 2163 } elseif ( $polylangTermModel ) { 2164 $polylangTermModel->set_language( $termId, $polylangLangCode ); 2165 } 2166 } 1900 2167 } 1901 2168 } else { … … 1930 2197 }; 1931 2198 1932 if ( function_exists( 'switch_to_blog' ) ) { 1933 switch_to_blog( $a2cData['store_id'] ); 1934 } 2199 $this->_initWooCommerceInMultiSiteContext( $a2cData['store_id'] ); 1935 2200 1936 2201 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) ) { … … 1967 2232 1968 2233 if ( is_wp_error( $term ) ) { 1969 throw new Exception( esc_html__( 'Can\'t create category! Error: ' . $term->get_error_message(), ' api2cart-bridge-connector' ) );2234 throw new Exception( esc_html__( 'Can\'t create category! Error: ' . $term->get_error_message(), 'bridge-connector' ) ); 1970 2235 } 1971 2236 … … 1980 2245 if ( $this->_wpmlEnabled ) { 1981 2246 $this->_translateTaxonomy( [$termId], $taxonomy, $activeLanguages, true ); 2247 } elseif ( $this->_polylangEnabled ) { 2248 $polylangTermModel = null; 2249 $polylangModel = $this->_getPolylangModel(); 2250 2251 if ( $polylangModel && isset( $polylangModel->term ) ) { 2252 $polylangTermModel = $polylangModel->term; 2253 } 2254 2255 $polylangLanguages = $this->_getPolylangLanguagesList( $polylangModel ); 2256 2257 $termLangCode = isset( $item['lang_code'] ) ? $item['lang_code'] : null; 2258 2259 if ( !$termLangCode && function_exists( 'pll_default_language' ) ) { 2260 $termLangCode = pll_default_language( 'slug' ); 2261 } 2262 2263 if ( !$termLangCode && $polylangLanguages ) { 2264 $termLangCode = $polylangLanguages[0]; 2265 } 2266 2267 $this->_polylangTranslateTaxonomy( [ $termId ], $taxonomy, $polylangLanguages, $polylangTermModel, $termLangCode ); 1982 2268 } 1983 2269 … … 2004 2290 update_term_meta( $trId, 'thumbnail_id', $attachmentId ); 2005 2291 } 2292 } 2293 } elseif ( $this->_polylangEnabled ) { 2294 $termIds = [ $termId ]; 2295 $translations = $this->_getPolylangTermTranslationIds( $termId ); 2296 2297 foreach ( $translations as $translationId ) { 2298 if ( (int)$translationId > 0 ) { 2299 $termIds[] = (int)$translationId; 2300 } 2301 } 2302 2303 foreach ( array_unique( $termIds ) as $translationId ) { 2304 update_term_meta( $translationId, 'thumbnail_id', $attachmentId ); 2006 2305 } 2007 2306 } else { … … 2036 2335 2037 2336 /** 2038 * @param array $a2cData Data2039 *2040 * @return array|mixed2041 */2337 * @param array $a2cData Data 2338 * 2339 * @return array|mixed 2340 */ 2042 2341 public function orderCalculate( $a2cData ) 2043 2342 { … … 2388 2687 2389 2688 /** 2390 * Image Add2391 *2392 2689 * @param array $a2cData Data 2393 2690 * … … 2399 2696 'error_code' => self::ERROR_CODE_SUCCESS, 2400 2697 'error' => null, 2401 'result' => array() ,2698 'result' => array() 2402 2699 ); 2403 2700 … … 2421 2718 try { 2422 2719 require_once( ABSPATH . 'wp-admin/includes/image.php' ); 2423 $productIds = $a2cData[ 'product_ids' ]; 2424 $productId = $a2cData[ 'product_ids' ][ 0 ]; 2425 $variantIds = $a2cData[ 'variant_ids' ]; 2720 $productIds = []; 2721 $variantIds = []; 2722 $categoryIds = []; 2723 $isProduct = true; 2724 $productId = 0; 2725 2726 if ( isset( $a2cData[ 'category_id' ] ) ) { 2727 $categoryIds = [ (int)$a2cData[ 'category_id' ] ]; 2728 $isProduct = false; 2729 } elseif ( isset( $a2cData[ 'category_ids' ] ) && is_array( $a2cData[ 'category_ids' ] ) ) { 2730 foreach ( $a2cData[ 'category_ids' ] as $catId ) { 2731 $categoryIds[] = (int)$catId; 2732 } 2733 $isProduct = false; 2734 } 2735 2736 if ( $isProduct === true ) { 2737 $productIds = $a2cData[ 'product_ids' ]; 2738 $productId = $a2cData[ 'product_ids' ][ 0 ]; 2739 $variantIds = $a2cData[ 'variant_ids' ]; 2740 } 2426 2741 2427 2742 $alt = ''; 2428 2743 2429 if ( !empty( $a2cData[ 'alt' ] )) {2744 if ( empty( $a2cData[ 'alt' ] ) === false ) { 2430 2745 $alt = $a2cData[ 'alt' ]; 2431 2746 } 2432 2747 2433 if ( function_exists( 'switch_to_blog' ) ) { 2434 switch_to_blog( $a2cData[ 'store_id' ] ); 2435 } 2436 2437 if ( $a2cData['content'] ) { 2438 $img = str_replace( 'data:image/jpeg;base64,', '', $a2cData[ 'content' ] ); 2439 $img = str_replace( ' ', '+', $img ); 2440 $decoded = base64_decode( $img ); 2748 $this->_initWooCommerceInMultiSiteContext( $a2cData[ 'store_id' ] ); 2749 2750 if ( $a2cData[ 'content' ] ) { 2751 $img = str_replace( 'data:image/jpeg;base64,', '', $a2cData[ 'content' ] ); 2752 $img = str_replace( ' ', '+', $img ); 2753 $decoded = base64_decode( $img ); 2441 2754 $filename = $a2cData[ 'name' ]; 2442 2755 … … 2444 2757 if ( $file[ 'error' ] !== false ) { 2445 2758 /* translators: %s: File name */ 2446 throw new Exception( sprintf( esc_html__( '[BRIDGE ERROR]: File save failed %s!', ' api2cart-bridge-connector' ), $filename ) );2759 throw new Exception( sprintf( esc_html__( '[BRIDGE ERROR]: File save failed %s!', 'bridge-connector' ), $filename ) ); 2447 2760 } 2448 2761 2449 2762 } elseif ( $a2cData['source'] ) { 2450 $imageUrl = $a2cData[ 'source' ];2763 $imageUrl = $a2cData[ 'source' ]; 2451 2764 $filename = $a2cData[ 'name' ]; 2452 2765 $parsedUrl = wp_parse_url( $imageUrl ); … … 2454 2767 if ( !$parsedUrl || !is_array( $parsedUrl ) ) { 2455 2768 /* translators: %s: Image Url */ 2456 throw new Exception( sprintf( esc_html__( '[BRIDGE ERROR]: Invalid URL %s!', ' api2cart-bridge-connector' ), $parsedUrl ) );2769 throw new Exception( sprintf( esc_html__( '[BRIDGE ERROR]: Invalid URL %s!', 'bridge-connector' ), $parsedUrl ) ); 2457 2770 } 2458 2771 2459 2772 $imageUrl = esc_url_raw( $imageUrl ); 2460 2773 2461 if ( !function_exists( 'download_url' )) {2774 if ( function_exists( 'download_url' ) === false ) { 2462 2775 include_once ABSPATH . 'wp-admin/includes/file.php'; 2463 2776 } … … 2469 2782 if ( is_wp_error( $fileArray[ 'tmp_name' ] ) ) { 2470 2783 throw new Exception( 2471 /* translators: %s: Image Url */2472 sprintf( esc_html__( '[BRIDGE ERROR]: Some error occurred while retrieving the remote image by URL: %s!', ' api2cart-bridge-connector' ), $imageUrl ) . ' ' . sprintf(2784 /* translators: %s: Image Url */ 2785 sprintf( esc_html__( '[BRIDGE ERROR]: Some error occurred while retrieving the remote image by URL: %s!', 'bridge-connector' ), $imageUrl ) . ' ' . sprintf( 2473 2786 /* translators: %s: Error message */ 2474 esc_html__( 'Error: %s', ' api2cart-bridge-connector' ),2787 esc_html__( 'Error: %s', 'bridge-connector' ), 2475 2788 $fileArray[ 'tmp_name' ]->get_error_message() 2476 2789 ) … … 2497 2810 2498 2811 if ( empty( $file['file'] ) ) { 2499 throw new Exception( esc_html__( '[BRIDGE ERROR]: No image has been uploaded!', 'api2cart-bridge-connector' ) ); 2500 } 2501 2502 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) ) { 2503 global $sitepress; 2504 2505 $sitepress = WPML\Container\make( '\SitePress' ); 2506 $currentLanguage = apply_filters( 'wpml_current_language', null ); 2507 2508 foreach ( $productIds as $productId ) { 2509 $objectId = apply_filters( 'wpml_object_id', $productId, 'post_product', true, $currentLanguage ); 2510 2511 if ( $objectId === null ) { 2512 continue; 2513 } 2514 2515 $trid = apply_filters( 'wpml_element_trid', null, $objectId, 'post_product' ); 2516 $translations = apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product', false, true ); 2517 2518 foreach ( $translations as $translation ) { 2519 if ( is_object( $translation ) ) { 2520 $productIds[] = $translation->{'element_id'}; 2521 } elseif ( is_array( $translation ) ) { 2522 $productIds[] = $translation[ 'element_id' ]; 2812 throw new Exception( esc_html__( '[BRIDGE ERROR]: No image has been uploaded!', 'bridge-connector' ) ); 2813 } 2814 2815 if ( $isProduct === true ) { 2816 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) ) { 2817 global $sitepress; 2818 2819 $sitepress = WPML\Container\make( '\SitePress' ); 2820 $currentLanguage = apply_filters( 'wpml_current_language', null ); 2821 2822 foreach ( $productIds as $productId ) { 2823 $objectId = apply_filters( 'wpml_object_id', $productId, 'post_product', true, $currentLanguage ); 2824 2825 if ( $objectId === null ) { 2826 continue; 2523 2827 } 2524 } 2525 } 2526 2527 foreach ( $variantIds as $variantId ) { 2528 $objectId = apply_filters( 'wpml_object_id', $variantId, 'post_product_variation', true, $currentLanguage ); 2529 2530 if ( $objectId === null ) { 2531 continue; 2532 } 2533 2534 $trid = apply_filters( 'wpml_element_trid', null, $objectId, 'post_product_variation' ); 2535 $translations = apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product_variation', false, true ); 2536 2537 foreach ( $translations as $translation ) { 2538 if ( is_object( $translation ) ) { 2539 $variantIds[] = $translation->{'element_id'}; 2540 } elseif ( is_array( $translation ) ) { 2541 $variantIds[] = $translation[ 'element_id' ]; 2828 2829 $trid = apply_filters( 'wpml_element_trid', null, $objectId, 'post_product' ); 2830 $translations = apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product', false, true ); 2831 2832 foreach ( $translations as $translation ) { 2833 if ( is_object( $translation ) ) { 2834 $productIds[] = $translation->{'element_id'}; 2835 } elseif ( is_array( $translation ) ) { 2836 $productIds[] = $translation[ 'element_id' ]; 2837 } 2542 2838 } 2543 2839 } 2544 } 2545 2546 $productIds = array_unique( $productIds ); 2547 $variantIds = array_unique( $variantIds ); 2840 2841 foreach ( $variantIds as $variantId ) { 2842 $objectId = apply_filters( 'wpml_object_id', $variantId, 'post_product_variation', true, $currentLanguage ); 2843 2844 if ( $objectId === null ) { 2845 continue; 2846 } 2847 2848 $trid = apply_filters( 'wpml_element_trid', null, $objectId, 'post_product_variation' ); 2849 $translations = apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product_variation', false, true ); 2850 2851 foreach ( $translations as $translation ) { 2852 if ( is_object( $translation ) ) { 2853 $variantIds[] = $translation->{'element_id'}; 2854 } elseif ( is_array( $translation ) ) { 2855 $variantIds[] = $translation[ 'element_id' ]; 2856 } 2857 } 2858 } 2859 2860 $productIds = array_unique( $productIds ); 2861 $variantIds = array_unique( $variantIds ); 2862 } 2863 } else { 2864 $applyToTranslations = true; 2865 2866 if ( isset( $a2cData[ 'apply_to_translations' ] ) ) { 2867 $applyToTranslations = (bool)$a2cData[ 'apply_to_translations' ]; 2868 } 2869 2870 $categoryIds = $this->_getCategoryImageTermIds( $categoryIds[ 0 ], $applyToTranslations ); 2871 } 2872 2873 if ( $isProduct ) { 2874 $attachmentParentId = $productId; 2875 } else { 2876 $attachmentParentId = 0; 2548 2877 } 2549 2878 2550 2879 $attachmentId = wp_insert_attachment( 2551 2880 [ 2552 'guid' => wp_upload_dir()[ 'url'] . '/' . basename( $file['file'] ),2881 'guid' => wp_upload_dir()[ 'url' ] . '/' . basename( $file[ 'file' ] ), 2553 2882 'post_mime_type' => $file[ 'type' ], 2554 2883 'post_title' => basename( $file[ 'file' ] ), … … 2557 2886 ], 2558 2887 $file[ 'file' ], 2559 $ productId2888 $attachmentParentId 2560 2889 ); 2561 2890 … … 2568 2897 wp_update_attachment_metadata( $attachmentId, $attachmentData ); 2569 2898 2570 foreach ( $productIds as $productId ) { 2571 if ( $a2cData[ 'is_thumbnail' ] ) { 2572 set_post_thumbnail( $productId, $attachmentId ); 2573 } 2574 2575 if ( $a2cData[ 'is_gallery' ] ) { 2576 $WCProduct = WC()->product_factory->get_product( $productId ); 2577 2578 if ( $WCProduct->get_type() !== 'variation' ) { 2579 $galleryIds = $WCProduct->get_gallery_image_ids(); 2580 $galleryIds[] = $attachmentId; 2581 $WCProduct->set_gallery_image_ids( array_unique( $galleryIds ) ); 2582 $WCProduct->save(); 2583 } 2584 } 2585 } 2586 2587 foreach ( $variantIds as $variantId ) { 2588 if ( $a2cData[ 'is_thumbnail' ] ) { 2589 set_post_thumbnail( $variantId, $attachmentId ); 2590 } 2591 2592 if ( $a2cData[ 'is_gallery' ] ) { 2593 $images = get_post_meta( $variantId, 'woo_variation_gallery_images', true );//https://wordpress.org/plugins/woo-variation-gallery support 2594 2595 if ( empty( $images ) ) { 2596 $variationGallery = [ $attachmentId ]; 2597 } else { 2598 $variationGallery = array_unique( array_merge( [ $attachmentId ], $images ) ); 2599 } 2600 2601 update_post_meta( $variantId, 'woo_variation_gallery_images', $variationGallery ); 2899 if ( $isProduct ) { 2900 foreach ( $productIds as $productId ) { 2901 if ( $a2cData[ 'is_thumbnail' ] ) { 2902 set_post_thumbnail( $productId, $attachmentId ); 2903 } 2904 2905 if ( $a2cData[ 'is_gallery' ] ) { 2906 $WCProduct = WC()->product_factory->get_product( $productId ); 2907 2908 if ( $WCProduct->get_type() !== 'variation' ) { 2909 $galleryIds = $WCProduct->get_gallery_image_ids(); 2910 $galleryIds[] = $attachmentId; 2911 $WCProduct->set_gallery_image_ids( array_unique( $galleryIds ) ); 2912 $WCProduct->save(); 2913 } 2914 } 2915 } 2916 2917 foreach ( $variantIds as $variantId ) { 2918 if ( $a2cData[ 'is_thumbnail' ] ) { 2919 set_post_thumbnail( $variantId, $attachmentId ); 2920 } 2921 2922 if ( $a2cData[ 'is_gallery' ] ) { 2923 $images = get_post_meta( $variantId, 'woo_variation_gallery_images', true );//https://wordpress.org/plugins/woo-variation-gallery support 2924 2925 if ( empty( $images ) ) { 2926 $variationGallery = [ $attachmentId ]; 2927 } else { 2928 $variationGallery = array_unique( array_merge( [ $attachmentId ], $images ) ); 2929 } 2930 2931 update_post_meta( $variantId, 'woo_variation_gallery_images', $variationGallery ); 2932 } 2933 } 2934 } else { 2935 foreach ( $categoryIds as $termId ) { 2936 update_term_meta( $termId, 'thumbnail_id', $attachmentId ); 2602 2937 } 2603 2938 } … … 2613 2948 2614 2949 return $response; 2950 } 2951 2952 /** 2953 * @param int $categoryId Category ID 2954 * @param bool $applyToTranslations Apply to all translations 2955 * 2956 * @return array 2957 */ 2958 protected function _getCategoryImageTermIds( $categoryId, $applyToTranslations ) 2959 { 2960 $categoryIds = array( $categoryId ); 2961 $translations = array(); 2962 2963 if ( $this->_wpmlEnabled ) { 2964 $translations = $this->_getWpmlTermTranslationIds( $categoryId, 'product_cat' ); 2965 } elseif ( $this->_polylangEnabled ) { 2966 $translations = $this->_getPolylangTermTranslationIds( $categoryId ); 2967 } 2968 2969 foreach ( $translations as $termId ) { 2970 $termId = (int)$termId; 2971 2972 if ( $termId > 0 ) { 2973 $categoryIds[] = $termId; 2974 } 2975 } 2976 2977 if ( $applyToTranslations ) { 2978 return array_values( array_unique( $categoryIds ) ); 2979 } 2980 2981 return array_values( array_unique( array( $categoryId ) ) ); 2982 } 2983 2984 /** 2985 * @param int $termId Term ID 2986 * @param string $taxonomy Taxonomy 2987 * 2988 * @return array 2989 */ 2990 protected function _getWpmlTermTranslationIds( $termId, $taxonomy ) 2991 { 2992 $categoryIds = array(); 2993 $taxonomyId = 0; 2994 $term = get_term( $termId, $taxonomy ); 2995 2996 if ( $term && !is_wp_error( $term ) ) { 2997 $taxonomyId = (int)$term->term_taxonomy_id; 2998 } 2999 3000 if ( $taxonomyId > 0 ) { 3001 $trid = apply_filters( 'wpml_element_trid', null, $taxonomyId, 'tax_' . $taxonomy ); 3002 3003 if ( $trid ) { 3004 $translations = apply_filters( 'wpml_get_element_translations', null, $trid, 'tax_' . $taxonomy, false, true ); 3005 3006 foreach ( $translations as $translation ) { 3007 $termTranslationId = 0; 3008 3009 if ( is_object( $translation ) && isset( $translation->term_id ) ) { 3010 $termTranslationId = (int)$translation->term_id; 3011 } elseif ( is_array( $translation ) && isset( $translation[ 'term_id' ] ) ) { 3012 $termTranslationId = (int)$translation[ 'term_id' ]; 3013 } 3014 3015 if ( $termTranslationId > 0 ) { 3016 $categoryIds[] = $termTranslationId; 3017 } 3018 } 3019 } 3020 } 3021 3022 return $categoryIds; 3023 } 3024 3025 /** 3026 * @param int $termId Term ID 3027 * 3028 * @return array 3029 */ 3030 protected function _getPolylangTermTranslationIds( $termId ) 3031 { 3032 $termModel = null; 3033 $categoryIds = array(); 3034 3035 if ( function_exists( 'PLL' ) ) { 3036 $pll = PLL(); 3037 3038 if ( $pll && isset( $pll->model ) && isset( $pll->model->term ) ) { 3039 $termModel = $pll->model->term; 3040 } 3041 } 3042 3043 $translations = $this->_getPolylangTermTranslations( $termId, $termModel ); 3044 3045 foreach ( $translations as $termId ) { 3046 $termId = (int)$termId; 3047 3048 if ( $termId > 0 ) { 3049 $categoryIds[] = $termId; 3050 } 3051 } 3052 3053 return $categoryIds; 2615 3054 } 2616 3055 … … 2635 3074 2636 3075 try { 2637 $response['result']['product_id'] = $this->_importProduct( $a2cData );3076 $response['result']['product_id'] = $this->_importProduct( $a2cData, 0, $productId ); 2638 3077 } catch ( Exception $e ) { 2639 3078 $this->_cleanGarbage( $productId ); … … 2671 3110 }; 2672 3111 2673 if ( function_exists( 'switch_to_blog' ) ) { 2674 switch_to_blog( $a2cData['store_id'] ); 2675 } 3112 $this->_initWooCommerceInMultiSiteContext( $a2cData['store_id'] ); 2676 3113 2677 3114 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) ) { … … 2788 3225 }; 2789 3226 2790 if ( function_exists( 'switch_to_blog' ) ) { 2791 switch_to_blog( $a2cData['store_id'] ); 2792 } 3227 $this->_initWooCommerceInMultiSiteContext( $a2cData['store_id'] ); 2793 3228 2794 3229 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) ) { … … 2884 3319 }; 2885 3320 2886 if ( function_exists( 'switch_to_blog' ) ) { 2887 switch_to_blog( $a2cData['store_id'] ); 2888 } 3321 $this->_initWooCommerceInMultiSiteContext( $a2cData['store_id'] ); 2889 3322 2890 3323 if ( defined( 'ICL_SITEPRESS_VERSION' ) && defined( 'ICL_PLUGIN_INACTIVE' ) && !ICL_PLUGIN_INACTIVE && class_exists( 'SitePress' ) ) { … … 2926 3359 2927 3360 /** 2928 * @param array $a2cData Data 2929 * @param int $id ID 2930 * 2931 * @return int 2932 * @throws Exception 2933 */ 2934 protected function _importProduct( array $a2cData, int $id = 0 ) { 2935 if ( function_exists( 'switch_to_blog' ) ) { 2936 switch_to_blog( $a2cData['store_id'] ); 2937 } 3361 * @param array $a2cData Data 3362 * @param int $id ID 3363 * @param int $productId Product ID 3364 * 3365 * @return int 3366 * @throws Exception 3367 */ 3368 protected function _importProduct( array $a2cData, int $id = 0, int &$productId = 0 ) { 3369 $this->_initWooCommerceInMultiSiteContext( $a2cData['store_id'] ); 2938 3370 2939 3371 if ( isset( $a2cData['product_data']['internal_data']['wpml_current_lang_id'] ) ) { … … 2945 3377 if ( !class_exists( $className ) ) { 2946 3378 /* translators: %s: Class name */ 2947 throw new Exception( sprintf( esc_html__( '[BRIDGE ERROR]: Class %s not exist!', ' api2cart-bridge-connector' ), $className ) );3379 throw new Exception( sprintf( esc_html__( '[BRIDGE ERROR]: Class %s not exist!', 'bridge-connector' ), $className ) ); 2948 3380 } 2949 3381 … … 2994 3426 remove_filter('content_save_pre', 'wp_filter_post_kses'); 2995 3427 } 2996 2997 3428 $product->save(); 2998 3429 … … 3014 3445 3015 3446 if ( isset( $a2cData['product_data']['terms_data'] ) ) { 3016 foreach ( $a2cData['product_data']['terms_data'] as $termName => $terms ) { 3017 $termIds = array_unique( array_column( $terms, 'name' ) ); 3018 $append = false; 3019 3020 if ( isset( $terms[0]['append'] ) ) { 3021 $append = (bool)$terms[0]['append']; 3022 } 3023 3024 if ( !$append ) { 3025 wp_delete_object_term_relationships( $productId, $termName ); 3026 wp_set_post_terms( $productId, $termIds, $termName ); 3447 $this->_setProductTerms( $productId, $a2cData['product_data']['terms_data'] ); 3448 } 3449 3450 if ( $this->_wpmlEnabled ) { 3451 $this->_wpmlSync( $product, $a2cData, $metaChanges ); 3452 } elseif ( $this->_polylangEnabled ) { 3453 $this->_polylangSync( $product, $a2cData, $id ); 3454 } 3455 3456 return $productId; 3457 } 3458 3459 /** 3460 * Reads attributes data and sets them to the product. 3461 * 3462 * @param int $productId Product ID 3463 * @param array $termsData Terms data 3464 * 3465 * @return void 3466 */ 3467 protected function _setProductTerms( $productId, array $termsData ) 3468 { 3469 foreach ( $termsData as $termName => $terms ) { 3470 $termIds = []; 3471 3472 foreach ( array_column( $terms, 'name' ) as $termId ) { 3473 if ( !empty( $termId ) ) { 3474 $termIds[] = $termId; 3475 } 3476 } 3477 3478 $termIds = array_values( array_unique( $termIds ) ); 3479 $append = false; 3480 3481 if ( isset( $terms[0]['append'] ) ) { 3482 $append = (bool)$terms[0]['append']; 3483 } 3484 3485 if ( empty( $termIds ) ) { 3486 continue; 3487 } 3488 3489 if ( $append ) { 3490 wp_set_post_terms( $productId, $termIds, $termName, true ); 3491 } else { 3492 wp_delete_object_term_relationships( $productId, $termName ); 3493 wp_set_post_terms( $productId, $termIds, $termName ); 3494 } 3495 } 3496 } 3497 3498 /** 3499 * Gets Polylang translation ID for the post by language code. 3500 * 3501 * @param int $postId Post ID 3502 * @param string $langCode Lang code 3503 * 3504 * @return int 3505 */ 3506 protected function _getPolylangTranslationId( $postId, $langCode ) 3507 { 3508 $translationId = 0; 3509 3510 if ( $langCode ) { 3511 if ( function_exists( 'pll_get_post' ) ) { 3512 $translationId = (int)pll_get_post( $postId, $langCode ); 3513 3514 if ( $translationId ) { 3515 return $translationId; 3516 } 3517 3518 if ( function_exists( 'pll_get_post_translations' ) ) { 3519 $translations = pll_get_post_translations( $postId ); 3520 3521 if ( is_array( $translations ) && isset( $translations[$langCode] ) ) { 3522 return (int)$translations[$langCode]; 3523 } 3524 } 3525 } 3526 } 3527 3528 return $translationId; 3529 } 3530 3531 /** 3532 * Initializes Polylang duplication compatibility by removing term setting on duplication. 3533 * @return void 3534 */ 3535 private 3536 function _initPolylangDuplicateProduct() 3537 { 3538 if ( class_exists( 'PLLWC_Admin_Product_Duplicate' ) && function_exists( 'PLL' ) ) { 3539 $pll = PLL(); 3540 3541 if ( $pll && isset( $pll->posts ) ) { 3542 // Mirrors Polylang-WC admin duplication behavior (admin/admin-product-duplicate.php). 3543 remove_action( 'set_object_terms', [ $pll->posts, 'set_object_terms' ] ); 3544 } 3545 } 3546 } 3547 3548 /** 3549 * Gets Polylang model instance if Polylang is active and model is accessible. 3550 * @return object|null 3551 */ 3552 protected function _getPolylangModel() 3553 { 3554 $model = null; 3555 $pll = null; 3556 3557 if ( function_exists( 'PLL' ) ) { 3558 $pll = PLL(); 3559 } 3560 3561 if ( $pll && isset( $pll->model ) ) { 3562 $model = $pll->model; 3563 } 3564 3565 return $model; 3566 } 3567 3568 /** 3569 * @param WC_Product $product WC Product 3570 * @param array $a2cData Data 3571 * @param int|null $sourceId Source ID 3572 * 3573 * @return void 3574 * @throws Exception 3575 */ 3576 protected function _polylangSync( WC_Product $product, $a2cData, $sourceId = 0 ) 3577 { 3578 $polylangData = $this->_initPolylangData( $a2cData ); 3579 $productId = $product->get_id(); 3580 $model = $this->_getPolylangModel(); 3581 $postModel = null; 3582 $termModel = null; 3583 3584 if ( is_object( $model ) ) { 3585 $postModel = isset( $model->post ) ? $model->post : null; 3586 $termModel = isset( $model->term ) ? $model->term : null; 3587 } 3588 3589 if ( $product->get_type() === 'variation' ) { 3590 $this->_polylangSyncVariations( $product, $polylangData, $postModel, $model ); 3591 return; 3592 } 3593 3594 $activeLanguages = $this->_getPolylangLanguagesList( $model ); 3595 3596 if ( $activeLanguages ) { 3597 $this->_polyLangTranslateTerms( $a2cData, $activeLanguages ); 3598 } 3599 3600 if ( !$polylangData['langCode'] && !$polylangData['onlyTranslateTo'] ) { 3601 $this->_handlePolylangWithoutTranslation( $product, $productId, $postModel, $model, $termModel ); 3602 return; 3603 } 3604 3605 $defaultLangPrId = $sourceId > 0 ? $sourceId : $productId; 3606 $createdIds = array(); 3607 3608 try { 3609 $skipSetLang = $this->_isSkipPolylangLanguageSet( $sourceId, $productId, $polylangData['langCode'], $postModel ); 3610 3611 if ( $polylangData['langCode'] && !$skipSetLang ) { 3612 $this->_polylangSetLanguage( $productId, $polylangData['langCode'], $postModel ); 3613 $this->_setPolylangLanguageForTerm( $productId, $polylangData['langCode'], $postModel, $model ); 3614 } 3615 3616 $this->_savePolylangProductTranslation( $sourceId, $productId, $polylangData['langCode'], $postModel ); 3617 3618 $termLangCode = $this->_polylangGetTermLanguage( $polylangData['langCode'], $skipSetLang, $productId, $postModel ); 3619 3620 if ( $termLangCode ) { 3621 $this->_setPolylangLanguageForProductTerms( $product, $termLangCode, $termModel ); 3622 } 3623 3624 $langList = $this->_normalizeLanguageList( $polylangData['onlyTranslateTo'] ); 3625 $productTranslations = $this->_preparePolylangProductTranslations( 3626 $product, 3627 $defaultLangPrId, 3628 $langList, 3629 $polylangData['langCode'], 3630 $postModel, 3631 $createdIds 3632 ); 3633 3634 if ( empty( $productTranslations ) ) { 3635 $productTranslations = $this->_getOrInitializePolylangTranslations( $defaultLangPrId, $postModel ); 3636 } 3637 3638 if ( $polylangData['langCode'] && $productId !== $defaultLangPrId && !isset( $productTranslations[$polylangData['langCode']] ) ) { 3639 $productTranslations[$polylangData['langCode']] = $productId; 3640 } 3641 3642 $termsData = $polylangData['termsData']; 3643 3644 if ( empty( $termsData ) ) { 3645 $termsData = $this->_getPolylangProductTermsData( $defaultLangPrId ); 3646 } 3647 3648 if ( $termsData && $productTranslations ) { 3649 $this->_syncPolylangProductTerms( $productTranslations, $termsData, $termModel ); 3650 } 3651 3652 if ( $product->get_type() === 'variable' ) { 3653 $this->_polylangSyncAttributes( $product, $polylangData, $postModel, $termModel, $model ); 3654 } 3655 3656 if ( $skipSetLang ) { 3657 $this->_rollbackPolylangBaseProtectedData( $defaultLangPrId, $a2cData, $postModel ); 3658 } 3659 } catch ( Exception $e ) { 3660 if ( $createdIds ) { 3661 $a2cData['product_data']['internal_data']['polylang_created_ids'] = $createdIds; 3662 $this->_rollbackProtectedData( $productId, $a2cData, null ); 3663 } 3664 throw $e; 3665 } 3666 } 3667 3668 /** 3669 * Sync Polylang translations for product variations 3670 * 3671 * @param WC_Product $variation Variation product 3672 * @param array $polylangData Polylang data 3673 * @param object|null $postModel Polylang post model 3674 * @param object|null $model Polylang main model 3675 * 3676 * @return void 3677 */ 3678 protected function _polylangSyncVariations( WC_Product $variation, $polylangData, $postModel, $model ) 3679 { 3680 $variationId = $variation->get_id(); 3681 $parentId = $variation->get_parent_id(); 3682 $languages = $this->_getPolylangLanguagesList( $model ); 3683 3684 if ( !$parentId ) { 3685 return; 3686 } 3687 3688 $parentTranslations = $this->_polylangGetTranslations( $parentId, $postModel ); 3689 3690 if ( empty( $parentTranslations ) ) { 3691 $langCode = $polylangData['langCode']; 3692 3693 if ( $langCode ) { 3694 $this->_polylangSetLanguage( $variationId, $langCode, $postModel ); 3695 } 3696 3697 return; 3698 } 3699 3700 $variationLangCode = $polylangData['langCode']; 3701 3702 if ( !$variationLangCode ) { 3703 $variationLangCode = $this->_polylangGetLanguage( $variationId, $postModel ); 3704 } 3705 3706 if ( !$variationLangCode ) { 3707 $variationLangCode = $this->_polylangGetLanguage( $parentId, $postModel ); 3708 } 3709 3710 if ( !$variationLangCode && function_exists( 'pll_default_language' ) ) { 3711 $variationLangCode = pll_default_language( 'slug' ); 3712 } 3713 3714 if ( $variationLangCode ) { 3715 $this->_polylangSetLanguage( $variationId, $variationLangCode, $postModel ); 3716 } 3717 3718 $variationTranslations = $this->_polylangGetTranslations( $variationId, $postModel ); 3719 3720 if ( empty( $variationTranslations ) && $variationLangCode ) { 3721 $variationTranslations = array( $variationLangCode => $variationId ); 3722 } 3723 3724 $langList = $this->_normalizeLanguageList( $polylangData['onlyTranslateTo'] ); 3725 3726 if ( empty( $langList ) ) { 3727 $langList = array_keys( $parentTranslations ); 3728 } 3729 3730 foreach ( $langList as $langCode ) { 3731 if ( isset( $variationTranslations[$langCode] ) ) { 3732 $trVariationId = $variationTranslations[$langCode]; 3733 3734 if ( $trVariationId != $variationId ) { 3735 $trVariation = wc_get_product( $trVariationId ); 3736 3737 if ( $trVariation instanceof WC_Product ) { 3738 $this->_polylangSyncVariationAttributes( $variation, $trVariation, $langCode, $model, $languages ); 3739 $trVariation->save(); 3740 } 3741 } 3742 3743 continue; 3744 } 3745 3746 if ( $langCode === $variationLangCode ) { 3747 continue; 3748 } 3749 3750 $trParentId = isset( $parentTranslations[$langCode] ) ? $parentTranslations[$langCode] : null; 3751 3752 if ( !$trParentId ) { 3753 continue; 3754 } 3755 3756 $trVariationId = $this->_duplicatePolylangVariation( $variation, $trParentId, $langCode, $postModel ); 3757 3758 if ( $trVariationId ) { 3759 $variationTranslations[$langCode] = $trVariationId; 3760 3761 $trVariation = wc_get_product( $trVariationId ); 3762 3763 if ( $trVariation instanceof WC_Product ) { 3764 $this->_polylangSyncVariationAttributes( $variation, $trVariation, $langCode, $model, $languages ); 3765 $trVariation->save(); 3766 } 3767 } 3768 } 3769 3770 if ( count( $variationTranslations ) > 1 ) { 3771 $this->_polylangSaveTranslations( $variationId, $variationTranslations, $postModel ); 3772 } 3773 } 3774 3775 3776 /** 3777 * Sync Polylang translations for product variations 3778 * 3779 * @param WC_Product $parentProduct Variation product 3780 * @param array $polylangData Polylang data 3781 * @param object|null $postModel Polylang post model 3782 * @param object|null $termModel Polylang term model 3783 * @param object|null $model Polylang main model 3784 * 3785 * @return void 3786 */ 3787 protected function _polylangSyncAttributes( WC_Product $parentProduct, $polylangData, $postModel, $termModel, $model ) 3788 { 3789 $parentId = $parentProduct->get_id(); 3790 3791 if ( $parentId === 0 ) { 3792 return; 3793 } 3794 3795 $parentTranslations = $this->_polylangGetTranslations( $parentId, $postModel ); 3796 3797 if ( $parentTranslations === [] || $parentTranslations === null ) { 3798 if ( $polylangData['langCode'] ) { 3799 $this->_polylangSetLanguage( $parentId, $polylangData['langCode'], $postModel ); 3800 } 3801 3802 return; 3803 } 3804 3805 $defaultLangCode = null; 3806 3807 if ( function_exists( 'pll_default_language' ) ) { 3808 $defaultLangCode = pll_default_language( 'slug' ); 3809 } elseif ( $model && method_exists( $model, 'get_default_language' ) ) { 3810 $langObj = $model->get_default_language(); 3811 3812 if ( $langObj && isset( $langObj->slug ) ) { 3813 $defaultLangCode = $langObj->slug; 3814 } 3815 } 3816 3817 if ( $defaultLangCode === null || $defaultLangCode === '' ) { 3818 return; 3819 } 3820 3821 if ( isset( $parentTranslations[$defaultLangCode] ) ) { 3822 $defaultParentId = (int)$parentTranslations[$defaultLangCode]; 3823 } else { 3824 return; 3825 } 3826 3827 $defaultParent = wc_get_product( $defaultParentId ); 3828 3829 if ( $defaultParent instanceof WC_Product ) { 3830 $attributes = $defaultParent->get_attributes(); 3831 } else { 3832 return; 3833 } 3834 3835 if ( $attributes === [] || $attributes === null ) { 3836 return; 3837 } 3838 3839 $activeLanguages = $this->_getPolylangLanguagesList( $model ); 3840 3841 if ( $activeLanguages ) { 3842 /** @var WC_Product_Attribute $attribute */ 3843 foreach ( $attributes as $attribute ) { 3844 if ( $attribute->is_taxonomy() ) { 3845 $terms = $attribute->get_terms(); 3846 $termIds = []; 3847 3848 if ( $terms ) { 3849 foreach ( $terms as $term ) { 3850 $termIds[] = (int)$term->term_id; 3851 } 3852 } 3853 3854 $termIds = array_values( array_unique( $termIds ) ); 3855 3856 if ( $termIds ) { 3857 $this->_polylangTranslateTaxonomy( $termIds, $attribute->get_taxonomy(), $activeLanguages, $termModel, $defaultLangCode ); 3858 } 3859 } 3860 } 3861 } 3862 3863 if ( class_exists( 'PLLWC_Products' ) && method_exists( 'PLLWC_Products', 'maybe_translate_attributes' ) ) { 3864 $attributes = PLLWC_Products::maybe_translate_attributes( $attributes, $defaultLangCode ); 3865 } 3866 3867 $defaultParent->set_attributes( $attributes ); 3868 $defaultParent->save(); 3869 3870 foreach ( $parentTranslations as $trLangCode => $trParentId ) { 3871 $trParentId = (int)$trParentId; 3872 3873 if ( $trLangCode === $defaultLangCode || $trParentId === 0 ) { 3874 continue; 3875 } 3876 3877 $trParent = wc_get_product( $trParentId ); 3878 3879 if ( $trParent instanceof WC_Product ) { 3880 $trAttributes = $attributes; 3881 3882 if ( class_exists( 'PLLWC_Products' ) && method_exists( 'PLLWC_Products', 'maybe_translate_attributes' ) ) { 3883 $trAttributes = PLLWC_Products::maybe_translate_attributes( $attributes, $trLangCode ); 3884 } 3885 3886 $trParent->set_attributes( $trAttributes ); 3887 $trParent->save(); 3888 } 3889 } 3890 } 3891 3892 /** 3893 * Duplicate a variation for Polylang translation 3894 * 3895 * @param WC_Product $variation Source variation 3896 * @param int $targetParentId Target parent product ID 3897 * @param string $langCode Target language code 3898 * @param object|null $postModel Polylang post model 3899 * 3900 * @return int 3901 */ 3902 protected function _duplicatePolylangVariation( WC_Product $variation, $targetParentId, $langCode, $postModel ) 3903 { 3904 $duplicateId = 0; 3905 3906 if ( !class_exists( 'WC_Product_Variation' ) ) { 3907 return $duplicateId; 3908 } 3909 3910 $targetParent = wc_get_product( $targetParentId ); 3911 3912 if ( !$targetParent || $targetParent->get_type() !== 'variable' ) { 3913 return $duplicateId; 3914 } 3915 3916 $newVariation = new WC_Product_Variation(); 3917 $newVariation->set_parent_id( $targetParentId ); 3918 3919 $newVariation->set_sku( $variation->get_sku() ); 3920 $newVariation->set_regular_price( $variation->get_regular_price() ); 3921 $newVariation->set_sale_price( $variation->get_sale_price() ); 3922 $newVariation->set_date_on_sale_from( $variation->get_date_on_sale_from() ); 3923 $newVariation->set_date_on_sale_to( $variation->get_date_on_sale_to() ); 3924 $newVariation->set_manage_stock( $variation->get_manage_stock() ); 3925 $newVariation->set_stock_quantity( $variation->get_stock_quantity() ); 3926 $newVariation->set_stock_status( $variation->get_stock_status() ); 3927 $newVariation->set_backorders( $variation->get_backorders() ); 3928 $newVariation->set_weight( $variation->get_weight() ); 3929 $newVariation->set_length( $variation->get_length() ); 3930 $newVariation->set_width( $variation->get_width() ); 3931 $newVariation->set_height( $variation->get_height() ); 3932 $newVariation->set_tax_class( $variation->get_tax_class() ); 3933 $newVariation->set_shipping_class_id( $variation->get_shipping_class_id() ); 3934 $newVariation->set_image_id( $variation->get_image_id() ); 3935 $newVariation->set_virtual( $variation->get_virtual() ); 3936 $newVariation->set_downloadable( $variation->get_downloadable() ); 3937 $newVariation->set_description( $variation->get_description() ); 3938 $newVariation->set_status( $variation->get_status() ); 3939 3940 $attributes = $variation->get_attributes(); 3941 3942 if ( $attributes ) { 3943 $newVariation->set_attributes( $attributes ); 3944 } 3945 3946 $duplicateId = $newVariation->save(); 3947 3948 if ( $duplicateId ) { 3949 if ( function_exists( 'pll_set_post_language' ) ) { 3950 pll_set_post_language( $duplicateId, $langCode ); 3951 } elseif ( $postModel ) { 3952 $postModel->set_language( $duplicateId, $langCode ); 3953 } 3954 3955 if ( function_exists( 'wc_delete_product_transients' ) ) { 3956 wc_delete_product_transients( $targetParentId ); 3957 } 3958 } 3959 3960 return $duplicateId; 3961 } 3962 3963 /** 3964 * Sync variation attributes to translation 3965 * 3966 * @param WC_Product $variation Source variation 3967 * @param WC_Product $trVariation Translated variation 3968 * @param string $langCode Target language code 3969 * @param object|null $model Polylang main model 3970 * @param array $languages Polylang languages 3971 * 3972 * @return void 3973 */ 3974 protected function _polylangSyncVariationAttributes( WC_Product $variation, WC_Product $trVariation, $langCode, $model, array $languages = array() ) 3975 { 3976 $attributes = $variation->get_attributes(); 3977 3978 if ( $attributes ) { 3979 $termModel = null; 3980 3981 if ( is_object( $model ) && isset( $model->term ) ) { 3982 $termModel = $model->term; 3983 } 3984 3985 $termIdsByTaxonomy = []; 3986 3987 if ( $languages ) { 3988 foreach ( $attributes as $attributeName => $attributeValue ) { 3989 if ( taxonomy_exists( $attributeName ) ) { 3990 $term = get_term_by( 'slug', $attributeValue, $attributeName ); 3991 3992 if ( $term && !is_wp_error( $term ) ) { 3993 if ( !isset( $termIdsByTaxonomy[$attributeName] ) ) { 3994 $termIdsByTaxonomy[$attributeName] = []; 3995 } 3996 3997 $termIdsByTaxonomy[$attributeName][] = (int)$term->term_id; 3998 } 3999 } 4000 } 4001 } 4002 4003 if ( $termIdsByTaxonomy ) { 4004 foreach ( $termIdsByTaxonomy as $taxonomy => $termIds ) { 4005 $termIds = array_values( array_unique( $termIds ) ); 4006 4007 if ( $termIds ) { 4008 $this->_polylangTranslateTaxonomy( $termIds, $taxonomy, $languages, $termModel, $langCode ); 4009 } 4010 } 4011 } 4012 4013 if ( class_exists( 'PLLWC_Products' ) 4014 && method_exists( 'PLLWC_Products', 'maybe_translate_attributes' ) 4015 && function_exists( 'pll_is_translated_taxonomy' ) 4016 ) { 4017 $translatedAttributes = PLLWC_Products::maybe_translate_attributes( $attributes, $langCode ); 4018 $trVariation->set_attributes( $translatedAttributes ); 4019 4020 if ( $imageId = $variation->get_image_id() ) { 4021 $trVariation->set_image_id( $imageId ); 4022 } 4023 4024 return; 4025 } 4026 4027 $translatedAttributes = array(); 4028 4029 foreach ( $attributes as $attributeName => $attributeValue ) { 4030 if ( taxonomy_exists( $attributeName ) ) { 4031 $term = get_term_by( 'slug', $attributeValue, $attributeName ); 4032 4033 if ( $term && !is_wp_error( $term ) ) { 4034 $translatedTermId = $this->_polylangGetTranslatedTermId( $term->term_id, $langCode, $termModel ); 4035 4036 if ( !$translatedTermId ) { 4037 $translatedTermId = $this->_createPolylangTermTranslation( $term->term_id, $langCode, $attributeName, $termModel ); 4038 } 4039 4040 if ( $translatedTermId ) { 4041 $translatedTerm = get_term_by( 'id', $translatedTermId, $attributeName ); 4042 4043 if ( $translatedTerm && !is_wp_error( $translatedTerm ) ) { 4044 $translatedAttributes[$attributeName] = $translatedTerm->slug; 4045 } else { 4046 $translatedAttributes[$attributeName] = $attributeValue; 4047 } 4048 4049 $trParentId = $trVariation->get_parent_id(); 4050 4051 if ( $trParentId ) { 4052 wp_set_object_terms( $trParentId, (int)$translatedTermId, $attributeName, true ); 4053 } 4054 } else { 4055 $translatedAttributes[$attributeName] = $attributeValue; 4056 } 4057 } else { 4058 $translatedAttributes[$attributeName] = $attributeValue; 4059 } 3027 4060 } else { 3028 wp_set_post_terms( $productId, $termIds, $termName, true ); 3029 } 3030 } 3031 } 3032 3033 $this->_wpmlSync( $product, $a2cData, $metaChanges ); 3034 3035 return $productId; 4061 $translatedAttributes[$attributeName] = $attributeValue; 4062 } 4063 } 4064 4065 $trVariation->set_attributes( $translatedAttributes ); 4066 4067 if ( $imageId = $variation->get_image_id() ) { 4068 $trVariation->set_image_id( $imageId ); 4069 } 4070 } 4071 } 4072 4073 /** 4074 * Get translated term ID for Polylang 4075 * 4076 * @param int $termId Term ID 4077 * @param string $langCode Target language code 4078 * @param object|null $termModel Polylang term model 4079 * 4080 * @return int 4081 */ 4082 protected function _polylangGetTranslatedTermId( $termId, $langCode, $termModel ) 4083 { 4084 $translatedId = 0; 4085 4086 if ( function_exists( 'pll_get_term' ) ) { 4087 $translatedId = pll_get_term( $termId, $langCode ); 4088 } elseif ( $termModel && method_exists( $termModel, 'get_translation' ) ) { 4089 $translatedId = $termModel->get_translation( $termId, $langCode ); 4090 } elseif ( $termModel && method_exists( $termModel, 'get_translations' ) ) { 4091 $translations = $termModel->get_translations( $termId ); 4092 4093 if ( isset( $translations[$langCode] ) ) { 4094 $translatedId = $translations[$langCode]; 4095 } 4096 } 4097 4098 return (int)$translatedId; 4099 } 4100 4101 /** 4102 * Initialize Polylang data from product data array 4103 * 4104 * @param array $a2cData A2C Data 4105 * 4106 * @return array 4107 */ 4108 private function _initPolylangData( array $a2cData ) 4109 { 4110 $internalData = array(); 4111 $termsData = array(); 4112 4113 if ( isset( $a2cData['product_data']['internal_data'] ) && is_array( $a2cData['product_data']['internal_data'] ) ) { 4114 $internalData = $a2cData['product_data']['internal_data']; 4115 } 4116 4117 if ( isset( $a2cData['product_data']['terms_data'] ) && is_array( $a2cData['product_data']['terms_data'] ) ) { 4118 $termsData = $a2cData['product_data']['terms_data']; 4119 } 4120 4121 return array( 4122 'langCode' => isset( $internalData['polylang_lang_code'] ) ? $internalData['polylang_lang_code'] : null, 4123 'onlyTranslateTo' => isset( $internalData['polylang_only_translate_to'] ) ? $internalData['polylang_only_translate_to'] : null, 4124 'termsData' => $termsData, 4125 ); 4126 } 4127 4128 /** 4129 * @param int $productId Product ID 4130 * 4131 * @return array 4132 */ 4133 protected function _getPolylangProductTermsData( $productId ) 4134 { 4135 $termsData = array(); 4136 $taxonomies = array( 'product_cat', 'product_tag' ); 4137 4138 foreach ( $taxonomies as $taxonomy ) { 4139 if ( $taxonomy === 'product_tag' ) { 4140 $terms = wp_get_post_terms( $productId, $taxonomy ); 4141 4142 if ( is_wp_error( $terms ) ) { 4143 continue; 4144 } 4145 4146 if ( $terms ) { 4147 $items = array(); 4148 4149 foreach ( $terms as $term ) { 4150 if ( isset( $term->name ) && $term->name !== '' ) { 4151 $items[] = array( 'name' => $term->name ); 4152 } 4153 } 4154 4155 if ( $items ) { 4156 $termsData[$taxonomy] = $items; 4157 } 4158 } 4159 } else { 4160 $termIds = wp_get_post_terms( $productId, $taxonomy, array( 'fields' => 'ids' ) ); 4161 4162 if ( is_wp_error( $termIds ) ) { 4163 continue; 4164 } 4165 4166 if ( $termIds ) { 4167 $items = array(); 4168 4169 foreach ( $termIds as $termId ) { 4170 $items[] = array( 'name' => (int)$termId ); 4171 } 4172 4173 if ( $items ) { 4174 $termsData[$taxonomy] = $items; 4175 } 4176 } 4177 } 4178 } 4179 4180 return $termsData; 4181 } 4182 4183 /** 4184 * Normalize language list to array format 4185 * 4186 * @param array|string $languages Language or languages list 4187 * 4188 * @return array 4189 */ 4190 private function _normalizeLanguageList( $languages ) 4191 { 4192 if ( !$languages ) { 4193 return array(); 4194 } 4195 4196 return is_array( $languages ) ? $languages : array( $languages ); 4197 } 4198 4199 /** 4200 * Get or initialize translations with fallback to base language 4201 * 4202 * @param int $postId Post ID 4203 * @param object|null $postModel Polylang post model 4204 * 4205 * @return array 4206 */ 4207 private function _getOrInitializePolylangTranslations( $postId, $postModel ) 4208 { 4209 $translations = $this->_polylangGetTranslations( $postId, $postModel ); 4210 4211 if ( !empty( $translations ) ) { 4212 return $translations; 4213 } 4214 4215 $baseLang = $this->_polylangGetLanguage( $postId, $postModel ); 4216 if ( $baseLang ) { 4217 return array( $baseLang => $postId ); 4218 } 4219 4220 return array(); 4221 } 4222 4223 /** 4224 * Determine if language set should be skipped 4225 * 4226 * @param int $sourceId Source product ID 4227 * @param int $productId Current product ID 4228 * @param string|null $langCode Language code 4229 * @param object|null $postModel Polylang post model 4230 * 4231 * @return bool 4232 */ 4233 private function _isSkipPolylangLanguageSet( $sourceId, $productId, $langCode, $postModel ) 4234 { 4235 if ( $sourceId <= 0 || !$langCode ) { 4236 return false; 4237 } 4238 4239 $sourceLang = $this->_polylangGetLanguage( $sourceId, $postModel ); 4240 $translationId = $this->_getPolylangTranslationId( $sourceId, $langCode ); 4241 4242 // Reset if source language differs and translation ID equals source ID 4243 if ( $sourceLang && $langCode !== $sourceLang && $translationId === $sourceId ) { 4244 $translationId = 0; 4245 } 4246 4247 // Skip if this is the first time and product equals source 4248 return $translationId === 0 && $productId === $sourceId; 4249 } 4250 4251 /** 4252 * Save product translation for new language 4253 * 4254 * @param int $sourceId Source product ID 4255 * @param int $productId Current product ID 4256 * @param string|null $langCode Language code 4257 * @param object|null $postModel 4258 * 4259 * @return void 4260 */ 4261 private function _savePolylangProductTranslation( $sourceId, $productId, $langCode, $postModel ) 4262 { 4263 if ( $sourceId <= 0 || $productId === $sourceId || !$langCode ) { 4264 return; 4265 } 4266 4267 $sourceLang = $this->_polylangGetLanguage( $sourceId, $postModel ); 4268 if ( !$sourceLang ) { 4269 return; 4270 } 4271 4272 $translations = $this->_polylangGetTranslations( $sourceId, $postModel ); 4273 4274 if ( $translations ) { 4275 $translations[$langCode] = $productId; 4276 } else { 4277 $translations = array( 4278 $sourceLang => $sourceId, 4279 $langCode => $productId, 4280 ); 4281 } 4282 4283 $this->_polylangSaveTranslations( $sourceId, $translations, $postModel ); 4284 } 4285 4286 /** 4287 * Process language variants and create duplicates as needed 4288 * 4289 * @param WC_Product $product WC Product 4290 * @param int $defaultLangPrId Default language product ID 4291 * @param array $langList Language list to process 4292 * @param string|null $langCode Current product language code 4293 * @param object|null $postModel Polylang post model 4294 * @param array $createdIds Reference to array for storing created product IDs for rollback 4295 * 4296 * @return array 4297 */ 4298 private function _preparePolylangProductTranslations( $product, $defaultLangPrId, $langList, $langCode, $postModel, &$createdIds ) 4299 { 4300 if ( !$langList ) { 4301 return array(); 4302 } 4303 4304 $productId = $product->get_id(); 4305 $translations = $this->_getOrInitializePolylangTranslations( $defaultLangPrId, $postModel ); 4306 4307 foreach ( $langList as $langItem ) { 4308 if ( isset( $translations[$langItem] ) ) { 4309 continue; 4310 } 4311 4312 if ( $langItem === $langCode && $productId !== $defaultLangPrId ) { 4313 $translations[$langItem] = $productId; 4314 } else { 4315 $duplicatePrId = $this->_duplicatePolylangProduct( $product, $langItem, $postModel ); 4316 4317 if ( $duplicatePrId ) { 4318 $createdIds[] = $duplicatePrId; 4319 $translations[$langItem] = $duplicatePrId; 4320 } 4321 } 4322 } 4323 4324 $this->_polylangSaveTranslations( $defaultLangPrId, $translations, $postModel ); 4325 4326 return $translations; 4327 } 4328 4329 /** 4330 * Handle Polylang sync when no language code is set 4331 * 4332 * @param WC_Product $product Product object 4333 * @param int $productId Product ID 4334 * @param object|null $postModel Polylang post model 4335 * @param object|null $model Polylang main model 4336 * @param object|null $termModel Polylang term model 4337 * 4338 * @return void 4339 */ 4340 private function _handlePolylangWithoutTranslation( $product, $productId, $postModel, $model, $termModel ) 4341 { 4342 $termLangCode = $this->_polylangGetTermLanguage( null, false, $productId, $postModel ); 4343 4344 if ( $termLangCode ) { 4345 $this->_setPolylangLanguageForTerm( $productId, $termLangCode, $postModel, $model ); 4346 $this->_setPolylangLanguageForProductTerms( $product, $termLangCode, $termModel ); 4347 } 4348 } 4349 4350 /** 4351 * Translate product terms based on active languages and product language code 4352 * 4353 * @param array $a2cData Data 4354 * @param array $languages Active languages 4355 * 4356 * @return void 4357 */ 4358 protected function _polyLangTranslateTerms( array $a2cData, array $languages ) 4359 { 4360 $model = $this->_getPolylangModel(); 4361 $activeLanguages = $languages; 4362 $internalData = []; 4363 $productLangCode = null; 4364 4365 if ( isset( $a2cData['product_data']['internal_data'] ) && is_array( $a2cData['product_data']['internal_data'] ) ) { 4366 $internalData = $a2cData['product_data']['internal_data']; 4367 } 4368 4369 if ( isset( $internalData['polylang_lang_code'] ) ) { 4370 $productLangCode = $internalData['polylang_lang_code']; 4371 } 4372 4373 if ( $productLangCode === null || $productLangCode === '' ) { 4374 if ( function_exists( 'pll_default_language' ) ) { 4375 $defaultLang = pll_default_language( 'slug' ); 4376 4377 if ( $defaultLang ) { 4378 $productLangCode = $defaultLang; 4379 } 4380 } 4381 } 4382 4383 if ( $activeLanguages && $model && isset( $model->term ) ) { 4384 if ( isset( $a2cData['product_data']['terms_data']['product_cat'] ) ) { 4385 $termNames = array_unique( array_column( $a2cData['product_data']['terms_data']['product_cat'], 'name' ) ); 4386 4387 if ( $termNames ) { 4388 $this->_polylangTranslateTaxonomy( $termNames, 'product_cat', $activeLanguages, $model->term, $productLangCode ); 4389 } 4390 } 4391 4392 if ( isset( $a2cData['product_data']['terms_data']['product_tag'] ) ) { 4393 $termNames = array_unique( array_column( $a2cData['product_data']['terms_data']['product_tag'], 'name' ) ); 4394 4395 if ( $termNames ) { 4396 $this->_polylangTranslateTaxonomy( $termNames, 'product_tag', $activeLanguages, $model->term, $productLangCode ); 4397 } 4398 } 4399 } 4400 } 4401 4402 /** 4403 * Get Polylang language for a post 4404 * 4405 * @param int $postId Post ID 4406 * @param string|null $langCode Language code 4407 * @param object|null $postModel Polylang post model 4408 * 4409 * @return void 4410 */ 4411 protected function _polylangSetLanguage( $postId, $langCode, $postModel ) 4412 { 4413 if ( $langCode && function_exists( 'pll_set_post_language' ) ) { 4414 pll_set_post_language( $postId, $langCode ); 4415 } elseif ( $langCode && $postModel ) { 4416 $postModel->set_language( $postId, $langCode ); 4417 } 4418 } 4419 4420 /** 4421 * Get Polylang language for a post 4422 * 4423 * @param int $postId Post ID 4424 * @param string|null $langCode Language code 4425 * @param object|null $postModel Polylang post model 4426 * @param object|null $model Polylang main model 4427 * 4428 * @return void 4429 */ 4430 protected function _setPolylangLanguageForTerm( $postId, $langCode, $postModel, $model ) 4431 { 4432 if ( $langCode ) { 4433 $currentLang = $this->_polylangGetLanguage( $postId, $postModel ); 4434 4435 if ( $currentLang === $langCode ) { 4436 return; 4437 } 4438 4439 if ( $model ) { 4440 $langObj = $model->get_language( $langCode ); 4441 4442 if ( $langObj && isset( $langObj->term_id ) ) { 4443 if ( function_exists( 'taxonomy_exists' ) && taxonomy_exists( 'language' ) ) { 4444 wp_set_object_terms( $postId, (int)$langObj->term_id, 'language' ); 4445 } 4446 } 4447 } 4448 } 4449 } 4450 4451 /** 4452 * Set Polylang language for product terms 4453 * 4454 * @param WC_Product $product WC Product 4455 * @param string|null $langCode Language code 4456 * @param object|null $termModel Polylang term model 4457 * 4458 * @return void 4459 */ 4460 protected function _setPolylangLanguageForProductTerms( WC_Product $product, $langCode, $termModel ) 4461 { 4462 if ( $langCode ) { 4463 $taxonomies = [ 'product_cat', 'product_tag' ]; 4464 $attributes = $product->get_attributes(); 4465 4466 foreach ( $attributes as $attribute ) { 4467 if ( is_object( $attribute ) && method_exists( $attribute, 'is_taxonomy' ) && $attribute->is_taxonomy() ) { 4468 $taxonomies[] = $attribute->get_taxonomy(); 4469 } 4470 } 4471 4472 $taxonomies = array_unique( $taxonomies ); 4473 $productId = $product->get_id(); 4474 4475 foreach ( $taxonomies as $taxonomy ) { 4476 $termIds = wp_get_post_terms( $productId, $taxonomy, [ 'fields' => 'ids' ] ); 4477 4478 if ( is_wp_error( $termIds ) ) { 4479 continue; 4480 } 4481 4482 if ( $termIds ) { 4483 foreach ( $termIds as $termId ) { 4484 $this->_setPolylangTermLanguage( (int)$termId, $langCode, $termModel ); 4485 } 4486 } 4487 } 4488 } 4489 } 4490 4491 /** 4492 * Map product terms to corresponding terms in the target language 4493 * 4494 * @param array $translations Language translations array 4495 * @param array $termsData Terms data array 4496 * @param object|null $termModel Polylang term model 4497 * 4498 * @return void 4499 */ 4500 protected function _syncPolylangProductTerms( array $translations, array $termsData, $termModel ) 4501 { 4502 if ( $translations && $termsData ) { 4503 $filteredTermsData = []; 4504 $nonTranslatedTerms = []; 4505 4506 foreach ( $termsData as $taxonomy => $items ) { 4507 $hasValues = false; 4508 4509 foreach ( $items as $item ) { 4510 if ( !empty( $item['name'] ) ) { 4511 $hasValues = true; 4512 break; 4513 } 4514 } 4515 4516 if ( $hasValues ) { 4517 $filteredTermsData[$taxonomy] = $items; 4518 4519 if ( isset( $items[0]['all_lang'] ) && $items[0]['all_lang'] === false ) { 4520 $nonTranslatedTerms[$taxonomy] = $items; 4521 } 4522 } 4523 } 4524 4525 if ( $filteredTermsData ) { 4526 foreach ( $translations as $langCode => $translationId ) { 4527 $termsToMap = $filteredTermsData; 4528 4529 if ( $nonTranslatedTerms ) { 4530 foreach ( $nonTranslatedTerms as $taxonomy => $items ) { 4531 if ( isset( $termsToMap[$taxonomy] ) ) { 4532 unset( $termsToMap[$taxonomy] ); 4533 } 4534 } 4535 } 4536 4537 if ( $termsToMap ) { 4538 $mappedTerms = $this->_mapPolylangTerms( $termsToMap, $langCode, $termModel ); 4539 4540 if ( $nonTranslatedTerms ) { 4541 $mappedTerms = array_merge( $mappedTerms, $nonTranslatedTerms ); 4542 } 4543 } else { 4544 $mappedTerms = $filteredTermsData; 4545 } 4546 4547 $this->_setProductTerms( $translationId, $mappedTerms ); 4548 } 4549 } 4550 } 4551 } 4552 4553 /** 4554 * Map product terms to corresponding terms in the target language 4555 * 4556 * @param int $termId Term ID 4557 * @param string|null $langCode Language code 4558 * @param object|null $termModel Polylang term model 4559 * 4560 * @return void 4561 */ 4562 protected function _setPolylangTermLanguage( $termId, $langCode, $termModel ) 4563 { 4564 if ( $langCode ) { 4565 $currentLang = null; 4566 4567 if ( function_exists( 'pll_get_term_language' ) ) { 4568 $currentLang = pll_get_term_language( $termId ); 4569 } elseif ( $termModel ) { 4570 $langObj = $termModel->get_language( $termId ); 4571 4572 if ( $langObj && isset( $langObj->slug ) ) { 4573 $currentLang = $langObj->slug; 4574 } 4575 } 4576 4577 if ( empty( $currentLang ) ) { 4578 if ( function_exists( 'pll_set_term_language' ) ) { 4579 pll_set_term_language( $termId, $langCode ); 4580 } elseif ( $termModel ) { 4581 $termModel->set_language( $termId, $langCode ); 4582 } 4583 } 4584 } 4585 } 4586 4587 /** 4588 * Get language code for product terms with multiple fallback options 4589 * 4590 * @param string|null $langCode Language code 4591 * @param bool $skipSetLang Whether to skip setting language for the product 4592 * @param int $productId Product ID 4593 * @param object|null $postModel Polylang post model 4594 * 4595 * @return string|null 4596 */ 4597 protected function _polylangGetTermLanguage( $langCode, $skipSetLang, $productId, $postModel = null ) 4598 { 4599 $termLangCode = null; 4600 $model = null; 4601 4602 if ( $postModel === null ) { 4603 $model = $this->_getPolylangModel(); 4604 4605 if ( $model && isset( $model->post ) ) { 4606 $postModel = $model->post; 4607 } 4608 } 4609 4610 if ( $langCode && !$skipSetLang ) { 4611 $termLangCode = $langCode; 4612 } 4613 4614 if ( empty( $termLangCode ) && $postModel ) { 4615 $termLangCode = $this->_polylangGetLanguage( $productId, $postModel ); 4616 } 4617 4618 if ( empty( $termLangCode ) ) { 4619 if ( function_exists( 'pll_default_language' ) ) { 4620 $termLangCode = pll_default_language( 'slug' ); 4621 } elseif ( $model && method_exists( $model, 'get_default_language' ) ) { 4622 $langObj = $model->get_default_language(); 4623 4624 if ( $langObj && isset( $langObj->slug ) ) { 4625 $termLangCode = $langObj->slug; 4626 } 4627 } 4628 } 4629 4630 return $termLangCode; 4631 } 4632 4633 /** 4634 * Get Polylang language for a post 4635 * 4636 * @param int $postId Post ID 4637 * @param object|null $postModel Polylang post model 4638 * 4639 * @return string|null 4640 */ 4641 protected function _polylangGetLanguage( $postId, $postModel ) 4642 { 4643 $lang = null; 4644 4645 if ( function_exists( 'pll_get_post_language' ) ) { 4646 $lang = pll_get_post_language( $postId ); 4647 } elseif ( $postModel ) { 4648 $langObj = $postModel->get_language( $postId ); 4649 4650 if ( $langObj && isset( $langObj->slug ) ) { 4651 $lang = $langObj->slug; 4652 } 4653 } 4654 4655 return $lang; 4656 } 4657 4658 /** 4659 * Get Polylang translations for a post 4660 * 4661 * @param int $postId Post ID 4662 * @param object|null $postModel Polylang post model 4663 * 4664 * @return array 4665 */ 4666 protected function _polylangGetTranslations( $postId, $postModel ) 4667 { 4668 $translations = []; 4669 4670 if ( function_exists( 'pll_get_post_translations' ) ) { 4671 $translations = pll_get_post_translations( $postId ); 4672 } elseif ( $postModel ) { 4673 $translations = $postModel->get_translations( $postId ); 4674 } 4675 4676 if ( is_array( $translations ) ) { 4677 return $translations; 4678 } 4679 4680 return []; 4681 } 4682 4683 /** 4684 * Save Polylang translations for a post 4685 * 4686 * @param int $sourceId Post ID of the source product 4687 * @param array $translations Translations array in format [lang_code => post_id] 4688 * @param object|null $postModel Polylang post model 4689 * 4690 * @return void 4691 */ 4692 protected function _polylangSaveTranslations( $sourceId, array $translations, $postModel ) 4693 { 4694 if ( $translations ) { 4695 if ( function_exists( 'pll_save_post_translations' ) ) { 4696 pll_save_post_translations( $translations ); 4697 } elseif ( $postModel ) { 4698 $postModel->save_translations( $sourceId, $translations ); 4699 } 4700 } 4701 } 4702 4703 /** 4704 * Duplicate product for Polylang translation 4705 * 4706 * @param WC_Product $product Product 4707 * @param string $langCode Language code 4708 * @param object|null $postModel Post model 4709 * 4710 * @return int 4711 */ 4712 protected function _duplicatePolylangProduct( WC_Product $product, $langCode, $postModel ) 4713 { 4714 $duplicatePrId = 0; 4715 $usePllWC = false; 4716 $pllWCDuplicator = null; 4717 4718 if ( $langCode && class_exists( 'WC_Admin_Duplicate_Product' ) ) { 4719 $this->_initPolylangDuplicateProduct(); 4720 4721 if ( class_exists( 'PLLWC_Admin_Product_Duplicate' ) ) { 4722 $usePllWC = true; 4723 $pllWCDuplicator = new PLLWC_Admin_Product_Duplicate(); 4724 } 4725 4726 $duplicator = new WC_Admin_Duplicate_Product(); 4727 $productDuplicate = $duplicator->product_duplicate( $product ); 4728 4729 if ( $productDuplicate instanceof WC_Product ) { 4730 if ( $usePllWC ) { 4731 $pllWCDuplicator->product_duplicate( $productDuplicate, $product ); 4732 } 4733 4734 $productDuplicate->set_name( $product->get_name() ); 4735 $productDuplicate->set_description( $product->get_description() ); 4736 $productDuplicate->set_short_description( $product->get_short_description() ); 4737 $productDuplicate->set_status( $product->get_status() ); 4738 $productDuplicate->save(); 4739 4740 $duplicatePrId = $productDuplicate->get_id(); 4741 4742 if ( $duplicatePrId ) { 4743 $origSku = $product->get_sku(); 4744 4745 if ( $origSku !== null && $origSku !== '' ) { 4746 update_post_meta( $duplicatePrId, '_sku', $origSku ); 4747 4748 if ( function_exists( 'wc_update_product_lookup_tables' ) ) { 4749 wc_update_product_lookup_tables( $duplicatePrId ); 4750 } 4751 4752 if ( function_exists( 'wc_delete_product_transients' ) ) { 4753 wc_delete_product_transients( $duplicatePrId ); 4754 } 4755 } 4756 4757 if ( function_exists( 'pll_set_post_language' ) ) { 4758 pll_set_post_language( $duplicatePrId, $langCode ); 4759 } elseif ( $postModel ) { 4760 $postModel->set_language( $duplicatePrId, $langCode ); 4761 } 4762 } 4763 } 4764 } 4765 4766 return $duplicatePrId; 4767 } 4768 4769 /** 4770 * Map product terms to corresponding terms in the target language 4771 * 4772 * @param array $termsData Terms data 4773 * @param string|null $langCode Lang code 4774 * @param object|null $termModel Term model 4775 * 4776 * @return array 4777 */ 4778 protected function _mapPolylangTerms( array $termsData, $langCode, $termModel = null ) 4779 { 4780 $result = $termsData; 4781 4782 if ( $langCode ) { 4783 $result = []; 4784 4785 foreach ( $termsData as $taxonomy => $terms ) { 4786 $mapped = []; 4787 4788 foreach ( $terms as $term ) { 4789 $termId = 0; 4790 $foundTermId = 0; 4791 4792 if ( $taxonomy === 'product_tag' ) { 4793 if ( isset( $term['name'] ) && $term['name'] !== '' ) { 4794 $foundTermId = $this->_findPolylangTermByNameAndLang( $term['name'], $taxonomy, $langCode, $termModel ); 4795 } 4796 4797 if ( $foundTermId ) { 4798 $termId = $foundTermId; 4799 } else { 4800 $termObj = get_term_by( 'name', $term['name'], $taxonomy ); 4801 4802 if ( $termObj instanceof WP_Term ) { 4803 $termId = (int)$termObj->term_id; 4804 } 4805 } 4806 } else { 4807 $termId = (int)$term['name']; 4808 } 4809 4810 if ( $termId ) { 4811 $termLangCode = null; 4812 4813 if ( function_exists( 'pll_get_term_language' ) ) { 4814 $termLangCode = pll_get_term_language( $termId ); 4815 } elseif ( $termModel && method_exists( $termModel, 'get_language' ) ) { 4816 $langObj = $termModel->get_language( $termId ); 4817 4818 if ( $langObj && isset( $langObj->slug ) ) { 4819 $termLangCode = $langObj->slug; 4820 } 4821 } 4822 4823 if ( $termLangCode === $langCode ) { 4824 $term['name'] = (int)$termId; 4825 $mapped[] = $term; 4826 continue; 4827 } 4828 4829 $trId = 0; 4830 4831 if ( function_exists( 'pll_get_term' ) ) { 4832 $trId = (int)pll_get_term( $termId, $langCode ); 4833 } elseif ( $termModel ) { 4834 $trId = (int)$termModel->get( $termId, $langCode ); 4835 } 4836 4837 if ( $trId ) { 4838 $term['name'] = (int)$trId; 4839 } else { 4840 if ( $foundTermId === 0 ) { 4841 $termObj = get_term( $termId, $taxonomy ); 4842 4843 if ( $termObj && !is_wp_error( $termObj ) ) { 4844 $foundTermId = $this->_findPolylangTermByNameAndLang( $termObj->name, $taxonomy, $langCode, $termModel ); 4845 } 4846 } 4847 4848 if ( $foundTermId ) { 4849 $term['name'] = (int)$foundTermId; 4850 } else { 4851 $createdTermId = $this->_createPolylangTermTranslation( $termId, $langCode, $taxonomy, $termModel ); 4852 4853 if ( $createdTermId ) { 4854 $term['name'] = (int)$createdTermId; 4855 } 4856 } 4857 } 4858 } 4859 4860 $mapped[] = $term; 4861 } 4862 4863 $result[$taxonomy] = $mapped; 4864 } 4865 } 4866 4867 return $result; 4868 } 4869 4870 /** 4871 * Find Polylang term by name and language 4872 * 4873 * @param string $termName Term name 4874 * @param string $taxonomy Taxonomy name 4875 * @param string|null $langCode Lang code 4876 * @param object|null $termModel Term model 4877 * 4878 * @return int 4879 */ 4880 protected function _findPolylangTermByNameAndLang( $termName, $taxonomy, $langCode, $termModel = null ) 4881 { 4882 if ( $termName && $langCode ) { 4883 $terms = get_terms( [ 4884 'taxonomy' => $taxonomy, 4885 'name' => $termName, 4886 'hide_empty' => false, 4887 ] ); 4888 4889 if ( is_wp_error( $terms ) ) { 4890 return 0; 4891 } 4892 4893 if ( $terms ) { 4894 foreach ( $terms as $term ) { 4895 $termLangCode = null; 4896 4897 if ( function_exists( 'pll_get_term_language' ) ) { 4898 $termLangCode = pll_get_term_language( $term->term_id ); 4899 } elseif ( $termModel && method_exists( $termModel, 'get_language' ) ) { 4900 $langObj = $termModel->get_language( $term->term_id ); 4901 4902 if ( $langObj && isset( $langObj->slug ) ) { 4903 $termLangCode = $langObj->slug; 4904 } 4905 } 4906 4907 if ( $termLangCode === $langCode ) { 4908 return (int)$term->term_id; 4909 } 4910 } 4911 } 4912 } 4913 4914 return 0; 4915 } 4916 4917 /** 4918 * Translate taxonomy terms for a product based on active languages and product language code 4919 * 4920 * @param array $termIds Term IDs or names 4921 * @param string $taxonomy Taxonomy name 4922 * @param array $languages Language slugs 4923 * @param object|null $termModel Term model 4924 * @param string|null $productLangCode Product language code 4925 * 4926 * @return void 4927 */ 4928 protected function _polylangTranslateTaxonomy( array $termIds, $taxonomy, array $languages, $termModel = null, $productLangCode = null ) 4929 { 4930 $terms = []; 4931 4932 foreach ( $termIds as $termId ) { 4933 if ( $taxonomy === 'product_tag' ) { 4934 $term = get_term_by( 'name', $termId, $taxonomy ); 4935 } else { 4936 $term = get_term_by( 'id', (int)$termId, $taxonomy ); 4937 } 4938 4939 if ( $term && !is_wp_error( $term ) ) { 4940 $terms[] = $term; 4941 } 4942 } 4943 4944 if ( $terms ) { 4945 foreach ( $terms as $term ) { 4946 $termLangCode = null; 4947 4948 if ( function_exists( 'pll_get_term_language' ) ) { 4949 $termLangCode = pll_get_term_language( $term->term_id ); 4950 } elseif ( $termModel && method_exists( $termModel, 'get_language' ) ) { 4951 $langObj = $termModel->get_language( $term->term_id ); 4952 4953 if ( $langObj && isset( $langObj->slug ) ) { 4954 $termLangCode = $langObj->slug; 4955 } 4956 } 4957 4958 if ( $termLangCode === null || $termLangCode === '' ) { 4959 if ( $productLangCode ) { 4960 if ( function_exists( 'pll_set_term_language' ) ) { 4961 pll_set_term_language( $term->term_id, $productLangCode ); 4962 } elseif ( $termModel && method_exists( $termModel, 'set_language' ) ) { 4963 $termModel->set_language( $term->term_id, $productLangCode ); 4964 } 4965 4966 $termLangCode = $productLangCode; 4967 } 4968 } 4969 4970 foreach ( $languages as $langCode ) { 4971 if ( $termLangCode && $termLangCode === $langCode ) { 4972 continue; 4973 } 4974 4975 $translationId = 0; 4976 4977 if ( function_exists( 'pll_get_term' ) ) { 4978 $translationId = (int)pll_get_term( $term->term_id, $langCode ); 4979 } elseif ( $termModel ) { 4980 $translationId = (int)$termModel->get( $term->term_id, $langCode ); 4981 } 4982 4983 if ( $translationId === 0 ) { 4984 $this->_createPolylangTermTranslation( $term->term_id, $langCode, $taxonomy, $termModel ); 4985 } 4986 } 4987 } 4988 } 4989 } 4990 4991 /** 4992 * Get Polylang languages list 4993 * 4994 * @param object|null $model Polylang model 4995 * 4996 * @return array 4997 */ 4998 protected function _getPolylangLanguagesList( $model = null ) 4999 { 5000 $languages = []; 5001 5002 if ( function_exists( 'pll_languages_list' ) ) { 5003 $languages = pll_languages_list( [ 'fields' => 'slug' ] ); 5004 } elseif ( $model && method_exists( $model, 'get_languages_list' ) ) { 5005 $languages = $model->get_languages_list( array( 'fields' => 'slug' ) ); 5006 } 5007 5008 if ( is_array( $languages ) ) { 5009 return $languages; 5010 } 5011 5012 return []; 5013 } 5014 5015 /** 5016 * Create Polylang term translation for a given term ID and language code 5017 * 5018 * @param int $termId Term ID 5019 * @param string|null $langCode Language code 5020 * @param string $taxonomy Taxonomy name 5021 * @param object|null $termModel Polylang term model 5022 * 5023 * @return int 5024 */ 5025 protected function _createPolylangTermTranslation( $termId, $langCode, $taxonomy, $termModel ) 5026 { 5027 if ( $langCode ) { 5028 $term = get_term( $termId, $taxonomy ); 5029 5030 if ( $term && !is_wp_error( $term ) ) { 5031 $translations = $this->_getPolylangTermTranslations( $termId, $termModel ); 5032 5033 if ( isset( $translations[$langCode] ) ) { 5034 return (int)$translations[$langCode]; 5035 } 5036 5037 if ( $translations === [] ) { 5038 $baseLangCode = null; 5039 5040 if ( function_exists( 'pll_get_term_language' ) ) { 5041 $baseLangCode = pll_get_term_language( $termId ); 5042 } elseif ( $termModel && method_exists( $termModel, 'get_language' ) ) { 5043 $langObj = $termModel->get_language( $termId ); 5044 5045 if ( $langObj && isset( $langObj->slug ) ) { 5046 $baseLangCode = $langObj->slug; 5047 } 5048 } 5049 5050 if ( $baseLangCode ) { 5051 $translations[$baseLangCode] = $termId; 5052 } else { 5053 if ( function_exists( 'pll_set_term_language' ) ) { 5054 pll_set_term_language( $termId, $langCode ); 5055 } elseif ( $termModel ) { 5056 $termModel->set_language( $termId, $langCode ); 5057 } 5058 5059 return $termId; 5060 } 5061 } 5062 5063 $args = []; 5064 5065 if ( is_taxonomy_hierarchical( $taxonomy ) && $term->parent ) { 5066 $parentTranslationId = $this->_createPolylangTermTranslation( $term->parent, $langCode, $taxonomy, $termModel ); 5067 5068 if ( $parentTranslationId ) { 5069 $args['parent'] = $parentTranslationId; 5070 } 5071 } 5072 5073 if ( !empty( $term->description ) ) { 5074 $args['description'] = $term->description; 5075 } 5076 5077 $termName = $term->name; 5078 $newTerm = null; 5079 $model = $this->_getPolylangModel(); 5080 5081 if ( $termModel && $model && method_exists( $termModel, 'insert' ) ) { 5082 $langObj = $model->get_language( $langCode ); 5083 5084 if ( $langObj ) { 5085 $newTerm = $termModel->insert( $termName, $taxonomy, $langObj, $args ); 5086 } 5087 } 5088 5089 $newTermId = 0; 5090 5091 if ( $newTerm === null ) { 5092 $newTerm = wp_insert_term( $termName, $taxonomy, $args ); 5093 } 5094 5095 if ( $newTerm instanceof WP_Error && $newTerm->get_error_data( 'term_exists' ) ) { 5096 $uniqueArgs = $args; 5097 $uniqueName = $termName . ' (' . $langCode . ')'; 5098 $uniqueSlug = ''; 5099 5100 if ( $term->slug ) { 5101 $uniqueSlug = $term->slug . '-' . $langCode; 5102 } 5103 5104 if ( $uniqueSlug ) { 5105 $uniqueArgs['slug'] = sanitize_title( $uniqueSlug ); 5106 } else { 5107 $uniqueArgs['slug'] = sanitize_title( $uniqueName ); 5108 } 5109 5110 $newTerm = null; 5111 5112 if ( $termModel && $model && method_exists( $termModel, 'insert' ) ) { 5113 $langObj = $model->get_language( $langCode ); 5114 5115 if ( $langObj ) { 5116 $newTerm = $termModel->insert( $uniqueName, $taxonomy, $langObj, $uniqueArgs ); 5117 } 5118 } 5119 5120 if ( $newTerm === null ) { 5121 $newTerm = wp_insert_term( $uniqueName, $taxonomy, $uniqueArgs ); 5122 } 5123 } 5124 5125 if ( $newTerm instanceof WP_Error ) { 5126 $termExistsId = $newTerm->get_error_data( 'term_exists' ); 5127 5128 if ( $termExistsId ) { 5129 $newTermId = (int)$termExistsId; 5130 } 5131 } elseif ( $newTerm instanceof WP_Term ) { 5132 $newTermId = (int)$newTerm->term_id; 5133 } elseif ( is_array( $newTerm ) && isset( $newTerm['term_id'] ) ) { 5134 $newTermId = (int)$newTerm['term_id']; 5135 } elseif ( is_int( $newTerm ) && $newTerm > 0 ) { 5136 $newTermId = $newTerm; 5137 } 5138 5139 if ( $newTermId > 0 ) { 5140 if ( function_exists( 'pll_set_term_language' ) ) { 5141 pll_set_term_language( $newTermId, $langCode ); 5142 } elseif ( $termModel ) { 5143 $termModel->set_language( $newTermId, $langCode ); 5144 } 5145 5146 $translations[$langCode] = $newTermId; 5147 $this->_savePolylangTermTranslations( $newTermId, $translations, $termModel ); 5148 5149 return $newTermId; 5150 } 5151 } 5152 } 5153 5154 return 0; 5155 } 5156 5157 /** 5158 * Get Polylang translations for a term 5159 * 5160 * @param int $termId Term ID 5161 * @param object|null $termModel Polylang term model 5162 * 5163 * @return array 5164 */ 5165 protected function _getPolylangTermTranslations( $termId, $termModel ) 5166 { 5167 $translations = []; 5168 5169 if ( function_exists( 'pll_get_term_translations' ) ) { 5170 $translations = pll_get_term_translations( $termId ); 5171 } elseif ( $termModel ) { 5172 $translations = $termModel->get_translations( $termId ); 5173 } 5174 5175 if ( is_array( $translations ) ) { 5176 return $translations; 5177 } 5178 5179 return []; 5180 } 5181 5182 /** 5183 * Save Polylang translations for a term 5184 * 5185 * @param int $termId Term ID 5186 * @param array $translations Translations array in format [lang_code => term_id] 5187 * @param object|null $termModel Polylang term model 5188 * 5189 * @return void 5190 */ 5191 protected function _savePolylangTermTranslations( $termId, array $translations, $termModel ) 5192 { 5193 if ( $translations ) { 5194 if ( function_exists( 'pll_save_term_translations' ) ) { 5195 pll_save_term_translations( $translations ); 5196 } elseif ( $termModel ) { 5197 $termModel->save_translations( $termId, $translations ); 5198 } 5199 } 3036 5200 } 3037 5201 … … 3227 5391 3228 5392 if ( !empty( $ids ) ) { 3229 foreach ( $ids as $ id ) {3230 if ( $ id && $id > 0 ) {3231 $upsells[] = $ id;5393 foreach ( $ids as $upsellId ) { 5394 if ( $upsellId && $upsellId > 0 ) { 5395 $upsells[] = $upsellId; 3232 5396 } 3233 5397 } … … 3242 5406 3243 5407 if ( !empty( $ids ) ) { 3244 foreach ( $ids as $ id ) {3245 if ( $ id && $id > 0 ) {3246 $crosssells[] = $ id;5408 foreach ( $ids as $crossSellId ) { 5409 if ( $crossSellId && $crossSellId > 0 ) { 5410 $crosssells[] = $crossSellId; 3247 5411 } 3248 5412 } … … 3254 5418 if ( isset( $data['product_data']['meta_data'] ) ) { 3255 5419 foreach ( $data['product_data']['meta_data'] as $metaKey => $metaData ) { 3256 if ( in_array( $metaKey, ['_product_attributes', '_upsell_ids', '_crosssell_ids' , '_sku'] ) ) {5420 if ( in_array( $metaKey, ['_product_attributes', '_upsell_ids', '_crosssell_ids'] ) ) { 3257 5421 continue; 3258 5422 } else { … … 3275 5439 3276 5440 if ( isset( $data['product_data']['terms_data'] ) ) { 3277 foreach ( $data['product_data']['terms_data'] as $termName => $terms ) { 3278 $termIds = array_unique( array_column( $terms, 'name' ) ); 3279 $append = false; 3280 3281 if ( isset( $terms[0]['append'] ) ) { 3282 $append = (bool)$terms[0]['append']; 3283 } 3284 3285 if ( !$append ) { 3286 wp_delete_object_term_relationships( $productId, $termName ); 3287 wp_set_post_terms( $productId, $termIds, $termName ); 3288 } else { 3289 wp_set_post_terms( $productId, $termIds, $termName, true ); 3290 } 3291 } 3292 } 3293 3294 if ( isset( $data['product_data']['internal_data']['no_wpml_sync'] ) && $data['product_data']['internal_data']['no_wpml_sync'] ) { 3295 return $product; 3296 } 3297 3298 $this->_wpmlSync( $product, $data, $metaChanges ); 5441 $this->_setProductTerms( $productId, $data['product_data']['terms_data'] ); 5442 } 5443 5444 if ( $this->_wpmlEnabled ) { 5445 $this->_wpmlSync( $product, $data, $metaChanges ); 5446 } elseif ( $this->_polylangEnabled ) { 5447 $this->_polylangSync( $product, $data, $id ); 5448 } 3299 5449 3300 5450 return $product; … … 3344 5494 /* translators: %s: attachment id */ 3345 5495 throw new WC_Data_Exception( 3346 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', ' api2cart-bridge-connector' ), $attachment_id )5496 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'bridge-connector' ), $attachment_id ) 3347 5497 ); 3348 5498 } … … 3664 5814 } 3665 5815 } else { 3666 $trAttributeValue = apply_filters( 'wpml_translate_single_string', $attributeValue, ' api2cart-bridge-connector', $attributeName, $langCode);5816 $trAttributeValue = apply_filters( 'wpml_translate_single_string', $attributeValue, 'bridge-connector', $attributeName, $langCode); 3667 5817 } 3668 5818 … … 3777 5927 $values = explode( WC_DELIMITER, $attribute['options'] ); 3778 5928 } 5929 5930 $existingValues = array(); 5931 $existingAttrs = $product->get_attributes(); 5932 5933 if ( $existingAttrs ) { 5934 foreach ( $existingAttrs as $existingAttr ) { 5935 if ( $existingAttr instanceof WC_Product_Attribute && !$existingAttr->is_taxonomy() ) { 5936 if ( $existingAttr->get_name() === $attributeName ) { 5937 $existingValues = (array)$existingAttr->get_options(); 5938 break; 5939 } 5940 } 5941 } 5942 } 5943 5944 if ( $existingValues ) { 5945 $values = array_merge( $existingValues, $values ); 5946 $values = array_values( array_unique( array_map( 'trim', $values ) ) ); 5947 } else { 5948 $values = array_values( array_unique( array_map( 'trim', $values ) ) ); 5949 } 5950 3779 5951 $attributeObject = new WC_Product_Attribute(); 3780 5952 $attributeObject->set_name( $attributeName ); … … 3933 6105 3934 6106 $wpdb->query( 3935 $wpdb->prepare(" 6107 $wpdb->prepare( " 6108 UPDATE {$wpdb->posts} 6109 SET {$fields} 6110 WHERE ID = %d", 6111 $values 6112 ) 6113 ); 6114 } 6115 } 6116 } 6117 6118 if ( isset( $a2cData['product_data']['internal_data']['polylang_created_ids'] ) ) { 6119 $createdIds = $a2cData['product_data']['internal_data']['polylang_created_ids']; 6120 6121 if ( is_array( $createdIds ) ) { 6122 foreach ( $createdIds as $createdId ) { 6123 $product = WC()->product_factory->get_product( (int)$createdId ); 6124 6125 if ( $product instanceof WC_Product ) { 6126 $product->delete( true ); 6127 } 6128 } 6129 } 6130 } 6131 } 6132 6133 /** 6134 * @param int $productId Product ID 6135 * @param array $a2cData Data 6136 * @param object|null $postModel Post model 6137 * 6138 * @return void 6139 */ 6140 protected function _rollbackPolylangBaseProtectedData( $productId, $a2cData, $postModel ) 6141 { 6142 global $wpdb; 6143 6144 if ( isset( $a2cData['product_data']['protected_data'] ) && is_array( $a2cData['product_data']['protected_data'] ) ) { 6145 $protectedData = $a2cData['product_data']['protected_data']; 6146 $langCode = $this->_polylangGetTermLanguage( null, false, $productId, $postModel ); 6147 $allowedFields = [ 6148 'post_title' => true, 6149 'post_content' => true, 6150 'post_excerpt' => true, 6151 'post_name' => true, 6152 'post_status' => true, 6153 ]; 6154 6155 $fieldsData = []; 6156 6157 if ( $langCode && isset( $protectedData[$langCode] ) && is_array( $protectedData[$langCode] ) ) { 6158 $fieldsData = $protectedData[$langCode]; 6159 } elseif ( array_intersect_key( $protectedData, $allowedFields ) ) { 6160 $fieldsData = $protectedData; 6161 } 6162 6163 if ( $fieldsData ) { 6164 $fieldsData = array_intersect_key( $fieldsData, $allowedFields ); 6165 $fieldAssignments = []; 6166 $values = []; 6167 6168 foreach ( $fieldsData as $field => $value ) { 6169 $fieldAssignments[] = "$field = %s"; 6170 $values[] = $value; 6171 } 6172 6173 if ( $fieldAssignments ) { 6174 $fields = implode( ', ', $fieldAssignments ); 6175 $values[] = (int)$productId; 6176 6177 $wpdb->query( 6178 $wpdb->prepare( " 3936 6179 UPDATE {$wpdb->posts} 3937 6180 SET {$fields} … … 3991 6234 $term_args['slug'] = $slug; 3992 6235 6236 if ( !empty( $term->description ) ) { 6237 $term_args['description'] = $term->description; 6238 } 6239 3993 6240 $new_term = wp_insert_term( $term_name, $termType, $term_args ); 3994 6241 … … 4059 6306 } 4060 6307 } else { 4061 $comment_author = esc_html__( 'WooCommerce', ' api2cart-bridge-connector' );4062 $comment_author_email = esc_html__( 'WooCommerce', ' api2cart-bridge-connector' ) . '@';6308 $comment_author = esc_html__( 'WooCommerce', 'bridge-connector' ); 6309 $comment_author_email = esc_html__( 'WooCommerce', 'bridge-connector' ) . '@'; 4063 6310 $comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : 'noreply.com'; 4064 6311 $comment_author_email = sanitize_email( $comment_author_email ); … … 5274 7521 if ( ! defined( 'ABSPATH' ) ) exit; 5275 7522 5276 define( 'A2CBC_BRIDGE_VERSION', '19 2' );7523 define( 'A2CBC_BRIDGE_VERSION', '198' ); 5277 7524 define( 'A2CBC_BRIDGE_DIRECTORY_NAME', basename( getcwd() ) ); 5278 define( 'A2CBC_BRIDGE_PUBLIC_KEY', '-----BEGIN PUBLIC KEY----- 5279 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzndeF8B8e+ElYjoRTBaC 5280 aNySz31gK3zasphhRlIOT6LwKQFutTYmWgkd4id2XwZE/wglsgnUXXmPcPp4+OdR 5281 6va+rgS3rtJA7I5DDj4oBVJ2cyOHgKtTnu5QnGXKM1nSBnx6t/BRZ+sVnTpKcyd0 5282 WtGakDIrzaouvOBYWFS5AT6LgWZnFj408Qx+UmAFQDR5oFAlkdCb1kUWSYhV1eNG 5283 vEAfIeDnFCbi+oyuzvMoMF8mFaNJ2MWfoPVHJl4TqnqK4RLTaYf3KLVAZrxniMQ2 5284 lN75G21rFG3ammcPtqiXiD6N034N0AviHCc2EkmmcFspH9qmOy/ITOyHHs1w9NNx 5285 uwIDAQAB 5286 -----END PUBLIC KEY----- 5287 '); 5288 define( 'A2CBC_BRIDGE_PUBLIC_KEY_ID', '4947ae9e4d350512cd240ec52d60c3a1' ); 5289 define( 'A2CBC_BRIDGE_ENABLE_ENCRYPTION', extension_loaded( 'openssl' ) ); 5290 5291 A2C_show_error( 0 ); 7525 define( 'A2CBC_BRIDGE_PUBLIC_KEY', '' ); 7526 define( 'A2CBC_BRIDGE_PUBLIC_KEY_ID', '' ); 7527 define( 'A2CBC_BRIDGE_ENABLE_ENCRYPTION', false ); 7528 7529 ini_set( 'display_errors', false ); 5292 7530 5293 7531 require_once 'config.php'; … … 5300 7538 die( 'ERROR_TOKEN_LENGTH' ); 5301 7539 } 5302 5303 function A2C_show_error( $status ) {5304 if ( $status ) {5305 if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) {5306 if ( substr( phpversion(), 0, 1 ) >= 5 ) {5307 error_reporting( E_ALL & ~E_STRICT );5308 return;5309 } else {5310 error_reporting( E_ALL );5311 return;5312 }5313 }5314 }5315 5316 error_reporting( 0 );5317 }5318 5319 /**5320 * A2C_exceptions_error_handler5321 *5322 * @param $severity5323 * @param $message5324 * @param $filename5325 * @param $lineno5326 *5327 * @throws ErrorException5328 */5329 function A2C_exceptions_error_handler( $severity, $message, $filename, $lineno ) {5330 if ( error_reporting() === 0 ) {5331 return;5332 }5333 5334 if ( strpos( $message, 'Declaration of' ) === 0 ) {5335 return;5336 }5337 5338 if ( error_reporting() & $severity ) {5339 throw new ErrorException( $message, 0, $severity, $filename, $lineno );5340 }5341 }5342 5343 set_error_handler( 'A2C_exceptions_error_handler' );5344 7540 5345 7541 /** … … 5393 7589 5394 7590 if ( !openssl_public_encrypt( $aesKey, $encryptedKey, A2CBC_BRIDGE_PUBLIC_KEY, OPENSSL_PKCS1_OAEP_PADDING ) ) { 5395 throw new Exception( esc_html__( 'ERROR_ENCRYPT', ' api2cart-bridge-connector' ) );7591 throw new Exception( esc_html__( 'ERROR_ENCRYPT', 'bridge-connector' ) ); 5396 7592 } 5397 7593 … … 5420 7616 5421 7617 if ( json_last_error() !== JSON_ERROR_NONE || !isset( $payload['encryptedKey'], $payload['iv'] ) ) { 5422 throw new Exception( esc_html__( 'ERROR_PARSE_DECRYPT', ' api2cart-bridge-connector' ) );7618 throw new Exception( esc_html__( 'ERROR_PARSE_DECRYPT', 'bridge-connector' ) ); 5423 7619 } 5424 7620 … … 5431 7627 5432 7628 if ( $result === false ) { 5433 throw new Exception( esc_html__( 'ERROR_DECRYPT', ' api2cart-bridge-connector' ) );7629 throw new Exception( esc_html__( 'ERROR_DECRYPT', 'bridge-connector' ) ); 5434 7630 } 5435 7631 -
api2cart-bridge-connector/trunk/readme.txt
r3439288 r3475673 4 4 Requires at least: 4.5 5 5 Tested up to: 6.8 6 Stable tag: 1.0.06 Stable tag: 3.0.11 7 7 License: GPLv2 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 97 97 = 3.0.8 =* Fixed product.variant.update method. Updated bridge to 188 version. 98 98 = 3.0.9 =* Updated bridge to 190 version. Fixed cart.meta_data.set method. 99 = 3.0.10 =*'Updated bridge to 192 version. Added compatibility with WooCommerce 10+ version. 99 = 3.0.10 =* Updated bridge to 192 version. Added compatibility with WooCommerce 10+ version. 100 = 3.0.11 =* Updated bridge to 198 version. Added Polylang multilingual plugin support.
Note: See TracChangeset
for help on using the changeset viewer.