Changeset 3374276
- Timestamp:
- 10/07/2025 10:22:48 AM (5 months ago)
- Location:
- wiser-review/trunk
- Files:
-
- 3 edited
-
readme.txt (modified) (2 diffs)
-
views/wiserw-plugin-settings.php (modified) (9 diffs)
-
wiser-review.php (modified) (31 diffs)
Legend:
- Unmodified
- Added
- Removed
-
wiser-review/trunk/readme.txt
r3367093 r3374276 6 6 Tested up to: 6.8 7 7 Requires PHP: 7.4 8 Stable tag: 2. 48 Stable tag: 2.5 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 133 133 = 2.4 = 134 134 Google Rich review schema-if zero review, Don't generate the schema, Other improvement 135 136 = 2.5 = 137 WPML & polylang support added. Fixed warning & optimized rich google snippt code -
wiser-review/trunk/views/wiserw-plugin-settings.php
r3359694 r3374276 46 46 <th class="name">Feature</th> 47 47 <th class="status">Enabled</th> 48 <th class="description">Description </th>48 <th class="description">Description & shortcode</th> 49 49 </tr> 50 50 </thead> … … 63 63 </td> 64 64 <td class="description"> 65 Enable Reviews65 Enable WiserReview 66 66 </td> 67 67 </tr> … … 87 87 </div> 88 88 </td> 89 <td class="description"> 90 Enter your WiserReview API key to activate product reviews, star ratings, and automated review requests. 91 If you don’t have an account with us, Create an account from <a href="https://app.wiserreview.com/signup" target="_new">here </a>. <br/><br/> 92 <a href="https://app.wiserreview.com/login" target="_new">Login in to WiserReview here </a> to get your API key from setting & manage reviews, customize widgets, and track review requests. 93 </td> 89 <td class="description"> 90 Enter your WiserReview API key to activate product reviews, star ratings, and automated review requests. 91 If you don’t have an account, create one <a href="https://app.wiserreview.com/signup" target="_blank">here</a>.<br><br> 92 <a href="https://app.wiserreview.com/login" target="_blank">Login to WiserReview</a> and go to 93 <a href="https://app.wiserreview.com/setting" target="_blank"><strong>Workspace > Settings</strong></a> to find your API key. 94 Also, you can <a href="https://app.wiserreview.com/reviewmoderate" target="_blank">manage reviews</a>, <a href="https://app.wiserreview.com/reviewWidget" target="_blank">customize widgets</a>, and <a href="https://app.wiserreview.com/sequence" target="_blank">send review requests</a>. 95 </td> 96 94 97 </tr> 95 98 <tr class="wiserrw_hide_row"> 96 99 <td> 97 100 <a href="#" class="wc-payment-gateway-method-title">Star Rating Count at PDP</a> 98 <p class="wiserrw_info"> Show Product Star Rating count on product page</p>101 <p class="wiserrw_info">Turn on to show the rating count below the product title (default position). Keep off if you’re using the shortcode.</p> 99 102 </td> 100 103 <td class="wisserrw_input_td"> … … 113 116 </div> 114 117 <p class="wiserrw_info">Use the given shortcode if you are using a cutomized theme or you want to change the position of the widget on the product page.</p> 118 <p class="wiserrw_info">To display on a custom product page, use <code>[wiserrw_rating_count id="123"]</code>. (replace 123 with the product ID).</p> 115 119 </td> 116 120 </tr> … … 118 122 <td> 119 123 <a href="#" class="wc-payment-gateway-method-title">Product Card Count At PLP</a> 120 <p class="wiserrw_info"> Show Product Star Rating count on collection page</p>124 <p class="wiserrw_info">Turn on to show the rating count below the product title on collection or listing pages (default position). Keep off if you’re using the shortcode.</p> 121 125 </td> 122 126 <td class="wisserrw_input_td"> … … 140 144 <td> 141 145 <a href="#" class="wc-payment-gateway-method-title">Product Review Section</a> 142 <p class="wiserrw_info"> Show Product Review section</p>146 <p class="wiserrw_info">Turn on to show the reviews block below the product summary (default position). Keep off if you’re using the shortcode.</p> 143 147 </td> 144 148 <td class="wisserrw_input_td"> … … 157 161 </div> 158 162 <p class="wiserrw_info">Use the given shortcode if you are using a cutomized theme or you want to change the position of the widget on the product page.</p> 163 <p class="wiserrw_info">To display on a custom product page, use <code>[wiserrw_product_review id="123"]</code>. (replace 123 with the product ID).</p> 159 164 </td> 160 165 </tr> … … 162 167 <td> 163 168 <a href="#" class="wc-payment-gateway-method-title">Review nudges</a> 164 <p class="wiserrw_info"> Show review nudges (Show selected featured carousel after call to action button )</p>169 <p class="wiserrw_info">Turn on to show review nudges below the main call-to-action button (default position). Keep off if you’re using the shortcode.</p> 165 170 </td> 166 171 <td class="wisserrw_input_td"> … … 179 184 </div> 180 185 <p class="wiserrw_info">Use the given shortcode if you are using a cutomized theme or you want to change the position of the widget on the product page.</p> 186 <p class="wiserrw_info">To display on a custom product page, use <code>[wiserrw_product_nudges id="123"]</code>. (replace 123 with the product ID).</p> 181 187 </td> 182 188 </tr> -
wiser-review/trunk/wiser-review.php
r3366418 r3374276 4 4 * Plugin URI: https://wiserreview.com 5 5 * Description: Wiser Review module helps you collect and display product reviews, star ratings, and nudges. It also automates review requests via email to boost custom engagement and conversions. 6 * Version: 2. 46 * Version: 2.5 7 7 * Author: Wiser Notify 8 8 * Requires Plugins: woocommerce … … 22 22 define( 'WISERRW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 23 23 define( 'WISERRW_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 24 define( 'WISERRW_PLUGIN_VERSION', 2. 4);24 define( 'WISERRW_PLUGIN_VERSION', 2.5 ); 25 25 define( 'WISERRW_API_HOST', 'https://api.wiserreview.com/api/woocommerce/' ); 26 27 if ( ! function_exists( 'wiserrw_load_textdomain' ) ) { 28 function wiserrw_load_textdomain() { 29 load_plugin_textdomain( 'wiser-review', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); 30 } 31 } 32 add_action( 'plugins_loaded', 'wiserrw_load_textdomain' ); 26 33 27 34 if ( ! function_exists( 'wiserrw_scripts' ) ) { … … 59 66 function wiserrw_api_settings() { 60 67 add_menu_page( 61 'Wiser Review',62 'Wiser Review',68 __( 'Wiser Review', 'wiser-review' ), 69 __( 'Wiser Review', 'wiser-review' ), 63 70 'manage_options', 64 71 'wiser-review', … … 74 81 */ 75 82 function wiserrw_api_settings_callback() { 76 wp_enqueue_style( 'woocommerce_admin_styles' ); 77 wp_enqueue_script( 'wiserrw-admin-script' ); 83 $wiserrw_api_settings = get_option('wiserrw_api_settings', array()); 84 if (!wp_style_is('woocommerce_admin_styles', 'registered')) { 85 return; 86 } 87 wp_enqueue_style('woocommerce_admin_styles'); 88 wp_enqueue_script('wiserrw-admin-script'); 78 89 include_once WISERRW_PLUGIN_DIR . '/views/wiserw-plugin-settings.php'; 79 90 } … … 106 117 107 118 function wiserrw_validate_api( $api_key ) { 108 $wiserrw_api_data = get_option( 'wiserrw_api_data', false ); 109 $url = 'https://api.wiserreview.com/api/woocommerce/verify?key=' . $api_key.'&wiserrw_hook_url='.get_site_url().'/wp-json/wiserrw/v1/reviews'; 110 $args = array( 111 'method' => 'GET', 112 'timeout' => 20 113 ); 114 $request = wp_remote_post( $url, $args ); 115 $response = json_decode( wp_remote_retrieve_body( $request ) ); 116 if (is_object($response) && isset($response->data) && isset($response->data->wsid)) { 117 update_option( 'wiserrw_wsid', $response->data->wsid ); 118 update_option( 'wiserrw_automation_id', $response->data->automation_id ); 119 } 120 return $response; 121 } 119 if ( empty( $api_key ) ) { 120 return false; 121 } 122 123 $url = 'https://api.wiserreview.com/api/woocommerce/verify?key=' . urlencode( $api_key ) . 124 '&wiserrw_hook_url=' . urlencode( get_site_url() . '/wp-json/wiserrw/v1/reviews' ) . 125 '&ver=' . urlencode( WISERRW_PLUGIN_VERSION ); 126 127 $args = array( 128 'method' => 'GET', 129 'timeout' => 20, 130 ); 131 132 $response = wp_remote_get( $url, $args ); 133 134 if ( is_wp_error( $response ) ) { 135 error_log( 'API Verify Error: ' . $response->get_error_message() ); 136 return false; 137 } 138 139 $body = json_decode( wp_remote_retrieve_body( $response ) ); 140 141 142 143 if ( is_object( $body ) && isset( $body->data ) && isset( $body->data->wsid ) ) { 144 update_option( 'wiserrw_api_data', $body->data ); 145 update_option( 'wiserrw_wsid', $body->data->wsid ); 146 update_option( 'wiserrw_automation_id', $body->data->automation_id ?? '' ); 147 } 148 149 return $body; 150 } 151 122 152 123 153 /** … … 191 221 */ 192 222 function wiserrw_show_review_nudges_section() { 193 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 194 if ( '1' === $wiserrw_api_settings['wiserrw_review_nudges_enabled'] ) { 195 echo do_shortcode( '[wiserrw_product_nudges]' ); 196 } 223 224 $wiserrw_api_settings = get_option('wiserrw_api_settings', array()); 225 226 // Check if the key exists and equals '1' 227 if (isset($wiserrw_api_settings['wiserrw_review_nudges_enabled']) 228 && '1' === $wiserrw_api_settings['wiserrw_review_nudges_enabled']) { 229 echo do_shortcode('[wiserrw_product_nudges]'); 230 } 197 231 } 198 232 add_action( 'woocommerce_after_add_to_cart_form', 'wiserrw_show_review_nudges_section' ); … … 201 235 * Shortcode for the Rating Count. 202 236 */ 203 function wiserrw_rating_count( ) {237 function wiserrw_rating_count( $atts ) { 204 238 ob_start(); 205 $product_id = get_the_ID(); 239 240 $atts = shortcode_atts( 241 array( 242 'id' => get_the_ID(), 243 ), 244 $atts, 245 'wiserrw_rating_count' 246 ); 247 248 $product_id = intval( $atts['id'] ); 249 if ( ! $product_id ) { 250 return ob_get_clean(); 251 } 252 253 // Get SKU directly (faster than wc_get_product) 254 $sku = get_post_meta( $product_id, '_sku', true ); 255 256 // Check settings 257 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 258 if ( ! $wiserrw_api_settings || '1' !== ( $wiserrw_api_settings['wiserrw_live_mode_checkbox'] ?? '' ) ) { 259 return ob_get_clean(); 260 } 261 262 // API HTML 263 $wiserrw_api_data = get_option( 'wiserrw_api_data' ); 264 $product_rating_count = $wiserrw_api_data->product_rating_count ?? ''; 265 $product_rating_count = preg_replace( '/data-pid=("|\')(.*?)\1/', 'data-pid="' . $product_id . '"', $product_rating_count ); 266 $product_rating_count = preg_replace( '/data-skuid=("|\')(.*?)\1/', 'data-skuid="' . $sku . '"', $product_rating_count ); 267 268 // --- Multilingual (cache per request) --- 269 static $lang_cache = array(); 270 if ( isset( $lang_cache[$product_id] ) ) { 271 $opid_value = $lang_cache[$product_id]['opid']; 272 $current_lang = $lang_cache[$product_id]['lang']; 273 } else { 274 $original_pid = 0; 275 $current_lang = ''; 276 277 // WPML 278 if ( has_filter( 'wpml_element_trid' ) ) { 279 $trid = apply_filters( 'wpml_element_trid', null, $product_id, 'post_product' ); 280 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : array(); 281 $current_lang = apply_filters( 'wpml_current_language', null ); 282 283 if ( $translations && is_array( $translations ) ) { 284 foreach ( $translations as $lang => $translation ) { 285 if ( ! empty( $translation->original ) ) { 286 $original_pid = (int) $translation->element_id; 287 } 288 } 289 } 290 } 291 292 // Polylang 293 if ( function_exists( 'pll_get_post' ) && function_exists( 'pll_current_language' ) ) { 294 $current_lang = pll_current_language(); 295 $default_lang = pll_default_language(); 296 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 297 $original_pid = pll_get_post( $product_id, $default_lang ); 298 } 299 } 300 301 // Fallback: current product if no original 302 $opid_value = ( $original_pid && $original_pid !== $product_id ) ? $original_pid : $product_id; 303 304 // Cache result 305 $lang_cache[$product_id] = array( 306 'opid' => $opid_value, 307 'lang' => $current_lang, 308 ); 309 } 310 311 // Extract wrapper 312 $wiser_open_tag = ''; 313 if ( preg_match( '/<div\b[^>]*>/i', $product_rating_count, $matches ) ) { 314 $wiser_open_tag = $matches[0]; 315 316 // Ensure opid 317 if ( preg_match( '/data-opid=/', $wiser_open_tag ) ) { 318 $wiser_open_tag = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $wiser_open_tag ); 319 } else { 320 $wiser_open_tag = str_replace( '<div', '<div data-opid="' . esc_attr( $opid_value ) . '"', $wiser_open_tag ); 321 } 322 323 // Ensure lang 324 if ( ! empty( $current_lang ) ) { 325 if ( preg_match( '/data-lang=/', $wiser_open_tag ) ) { 326 $wiser_open_tag = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $wiser_open_tag ); 327 } else { 328 $wiser_open_tag = str_replace( '<div', '<div data-lang="' . esc_attr( $current_lang ) . '"', $wiser_open_tag ); 329 } 330 } 331 } 332 333 // Inner HTML: prefer post meta, patch if missing 334 $meta_html = get_post_meta( $product_id, 'wiserrw_star_rating_html', true ); 335 if ( $meta_html ) { 336 // Patch opid 337 if ( preg_match( '/data-opid=/', $meta_html ) ) { 338 $meta_html = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $meta_html ); 339 } else { 340 $meta_html = preg_replace( '/<div\b/i', '<div data-opid="' . esc_attr( $opid_value ) . '"', $meta_html, 1 ); 341 } 342 343 // Patch lang 344 if ( ! empty( $current_lang ) ) { 345 if ( preg_match( '/data-lang=/', $meta_html ) ) { 346 $meta_html = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $meta_html ); 347 } else { 348 $meta_html = preg_replace( '/<div\b/i', '<div data-lang="' . esc_attr( $current_lang ) . '"', $meta_html, 1 ); 349 } 350 } 351 352 $inner_html = "<!-- Loaded from post meta (patched) -->" . $meta_html; 353 } else { 354 $inner_html = "<!-- Loaded from API fallback -->" . $product_rating_count; 355 } 356 357 // Output 358 if ( $wiser_open_tag ) { 359 echo $wiser_open_tag; 360 } 361 echo $inner_html; 362 echo "</div>"; 363 364 return ob_get_clean(); 365 } 366 add_shortcode( 'wiserrw_rating_count', 'wiserrw_rating_count' ); 367 368 369 370 371 /** 372 * Shortcode for the product. 373 */ 374 375 376 377 function wiserrw_product_review( $atts ) { 378 ob_start(); 379 380 $atts = shortcode_atts( 381 array( 382 'id' => get_the_ID(), 383 ), 384 $atts, 385 'wiserrw_product_review' 386 ); 387 388 $product_id = intval( $atts['id'] ); 389 if ( ! $product_id ) { 390 return ob_get_clean(); 391 } 392 206 393 $product = wc_get_product( $product_id ); 207 394 if ( ! $product ) { 208 return; 209 } 210 $sku = $product->get_sku(); 395 return ob_get_clean(); 396 } 211 397 212 398 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 213 if ( $wiserrw_api_settings && '1' === $wiserrw_api_settings['wiserrw_live_mode_checkbox'] ) { 214 215 $wiserrw_api_data = get_option( 'wiserrw_api_data' ); 216 $product_rating_count_pid = preg_replace( "/data-pid='(.*?)'/", "data-pid='$product_id'", $wiserrw_api_data->product_rating_count ); 217 $product_rating_count = preg_replace( "/data-skuid='(.*?)'/", "data-skuid='$sku'", $product_rating_count_pid ); 218 $allowed_tags = array( 219 'div' => array( 220 'data-type' => true, 221 'class' => true, 222 'data-platform' => true, 223 'data-pid' => true, 224 'data-skuid' => true, 225 ), 226 ); 227 228 $parent_html = ''; 229 if ( preg_match( '/<div\b[^>]*>/i', $product_rating_count, $matches ) ) { 230 $wiser_open_tag = $matches[0]; 231 } 232 $product_rating_count = get_post_meta( $product_id, 'wiserrw_star_rating_html', true ); 233 234 echo $wiser_open_tag; 235 if( $product_rating_count ) { 236 237 echo $product_rating_count; 238 239 } 240 echo "</div>"; 241 $output = ob_get_contents(); 399 if ( ! $wiserrw_api_settings || '1' !== ( $wiserrw_api_settings['wiserrw_live_mode_checkbox'] ?? '' ) ) { 242 400 return ob_get_clean(); 243 401 } 244 } 245 add_shortcode( 'wiserrw_rating_count', 'wiserrw_rating_count' ); 246 247 /** 248 * Shortcode for the Rating Count. 249 */ 250 function wiserrw_product_review() { 251 ob_start(); 252 $product_id = get_the_ID(); 253 $product = wc_get_product( $product_id ); 254 if ( ! $product ) { 255 return; 256 } 257 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 258 if ( $wiserrw_api_settings && '1' === $wiserrw_api_settings['wiserrw_live_mode_checkbox'] ) { 259 260 $sku = $product->get_sku(); 261 $wiserrw_api_data = get_option( 'wiserrw_api_data' ); 262 if ( is_user_logged_in() ) { 263 $data_in_value = 'true'; 402 403 $sku = $product->get_sku(); 404 $data_login = is_user_logged_in() ? 'true' : 'false'; 405 $wiserrw_api_data = get_option( 'wiserrw_api_data' ); 406 407 // --- Multilingual (WPML / Polylang) --- 408 $original_pid = 0; 409 $current_lang = ''; 410 411 // WPML 412 if ( has_filter( 'wpml_element_trid' ) ) { 413 $trid = apply_filters( 'wpml_element_trid', null, $product_id, 'post_product' ); 414 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : array(); 415 $current_lang = apply_filters( 'wpml_current_language', null ); 416 417 if ( $translations && is_array( $translations ) ) { 418 foreach ( $translations as $lang => $translation ) { 419 if ( ! empty( $translation->original ) ) { 420 $original_pid = (int) $translation->element_id; 421 } 422 } 423 } 424 } 425 426 // Polylang 427 if ( function_exists( 'pll_get_post' ) && function_exists( 'pll_current_language' ) ) { 428 $current_lang = pll_current_language(); 429 $default_lang = pll_default_language(); 430 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 431 $original_pid = pll_get_post( $product_id, $default_lang ); 432 } 433 } 434 435 // Fallback: use current product if no original found 436 $opid_value = ( $original_pid && $original_pid !== $product_id ) ? $original_pid : $product_id; 437 438 // --- Patch API HTML --- 439 $product_review_section = $wiserrw_api_data->product_review_section ?? ''; 440 441 // Ensure data-login 442 if ( preg_match( '/data-login=/', $product_review_section ) ) { 443 $product_review_section = preg_replace( '/data-lgin=("|\')(.*?)\1/', 'data-login="' . $data_login . '"', $product_review_section ); 444 } else { 445 $product_review_section = preg_replace( '/<div\b/i', '<div data-lgin="' . esc_attr( $data_login ) . '"', $product_review_section, 1 ); 446 } 447 448 // Ensure pid 449 $product_review_section = preg_replace( '/data-pid=("|\')(.*?)\1/', 'data-pid="' . $product_id . '"', $product_review_section ); 450 451 // Ensure skuid 452 $product_review_section = preg_replace( '/data-skuid=("|\')(.*?)\1/', 'data-skuid="' . $sku . '"', $product_review_section ); 453 454 // Ensure opid 455 if ( preg_match( '/data-opid=/', $product_review_section ) ) { 456 $product_review_section = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $product_review_section ); 457 } else { 458 $product_review_section = preg_replace( '/<div\b/i', '<div data-opid="' . esc_attr( $opid_value ) . '"', $product_review_section, 1 ); 459 } 460 461 // Ensure lang 462 if ( ! empty( $current_lang ) ) { 463 if ( preg_match( '/data-lang=/', $product_review_section ) ) { 464 $product_review_section = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $product_review_section ); 264 465 } else { 265 $data_in_value = 'false'; 266 } 267 268 // If data-login exists, replace it; otherwise, add it to the first tag 269 if ( preg_match( "/data-lgin=['\"](.*?)['\"]/", $wiserrw_api_data->product_review_section ) ) { 270 $product_review_section_loggedin = preg_replace( 271 "/data-lgin=['\"](.*?)['\"]/", 272 "data-lgin='{$data_in_value}'", 273 $wiserrw_api_data->product_review_section 274 ); 275 } else { 276 $product_review_section_loggedin = preg_replace( 277 '/<(\w+)/', 278 "<$1 data-lgin='{$data_in_value}'", 279 $wiserrw_api_data->product_review_section, 280 1 // only add to the first tag 281 ); 282 } 283 $product_review_section_pid = preg_replace( "/data-pid='(.*?)'/", "data-pid='$product_id'", $product_review_section_loggedin ); 284 285 $product_review_section = preg_replace( "/data-skuid='(.*?)'/", "data-skuid='$sku'", $product_review_section_pid ); 286 287 $product_rating_count = preg_replace( "/data-pid='(.*?)'/", "data-pid='$product_id'", $wiserrw_api_data->product_rating_count ); 288 $product_rating_count = preg_replace( "/data-skuid='(.*?)'/", "data-skuid='$sku'", $wiserrw_api_data->product_rating_count ); 289 290 $nudges_section = preg_replace( "/data-pid='(.*?)'/", "data-pid='$product_id'", $wiserrw_api_data->nudges_section ); 291 $nudges_section = preg_replace( "/data-skuid='(.*?)'/", "data-skuid='$sku'", $wiserrw_api_data->nudges_section ); 292 293 $allowed_tags = array( 294 'div' => array( 295 'data-type' => true, 296 'class' => true, 297 'data-platform' => true, 298 'data-pid' => true, 299 'data-skuid' => true, 300 ), 301 ); 302 echo $product_review_section; 303 } 304 $output = ob_get_contents(); 466 $product_review_section = preg_replace( '/<div\b/i', '<div data-lang="' . esc_attr( $current_lang ) . '"', $product_review_section, 1 ); 467 } 468 } 469 470 // Output 471 echo $product_review_section; 472 305 473 return ob_get_clean(); 306 474 } … … 310 478 * Shortcode for the Nudges. 311 479 */ 312 function wiserrw_product_nudges( ) {480 function wiserrw_product_nudges( $atts = [] ) { 313 481 ob_start(); 482 314 483 $product_id = get_the_ID(); 315 $product = wc_get_product( $product_id );484 $product = wc_get_product( $product_id ); 316 485 if ( ! $product ) { 317 return; 318 } 486 return ob_get_clean(); 487 } 488 319 489 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 320 if ( $wiserrw_api_settings && '1' === $wiserrw_api_settings['wiserrw_live_mode_checkbox'] ) { 321 $wiserrw_api_data = get_option( 'wiserrw_api_data' ); 322 $sku = $product->get_sku(); 323 324 $wiserrw_api_data = get_option( 'wiserrw_api_data' ); 325 $nudges_section_pid = preg_replace( "/data-pid='(.*?)'/", "data-pid='$product_id'", $wiserrw_api_data->nudges_section ); 326 $nudges_section = preg_replace( "/data-skuid='(.*?)'/", "data-skuid='$sku'", $nudges_section_pid ); 327 $allowed_tags = array( 328 'div' => array( 329 'data-type' => true, 330 'class' => true, 331 'data-platform' => true, 332 'data-pid' => true, 333 'data-skuid' => true, 334 ), 335 ); 336 echo $nudges_section; 337 } 338 $output = ob_get_contents(); 490 if ( ! $wiserrw_api_settings || '1' !== ( $wiserrw_api_settings['wiserrw_live_mode_checkbox'] ?? '' ) ) { 491 return ob_get_clean(); 492 } 493 494 $sku = $product->get_sku(); 495 $wiserrw_api_data = get_option( 'wiserrw_api_data' ); 496 497 // --- Multilingual (WPML / Polylang) --- 498 $original_pid = 0; 499 $current_lang = ''; 500 501 // WPML 502 if ( has_filter( 'wpml_element_trid' ) ) { 503 $trid = apply_filters( 'wpml_element_trid', null, $product_id, 'post_product' ); 504 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : array(); 505 $current_lang = apply_filters( 'wpml_current_language', null ); 506 507 if ( $translations && is_array( $translations ) ) { 508 foreach ( $translations as $lang => $translation ) { 509 if ( ! empty( $translation->original ) ) { 510 $original_pid = (int) $translation->element_id; 511 } 512 } 513 } 514 } 515 516 // Polylang 517 if ( function_exists( 'pll_get_post' ) && function_exists( 'pll_current_language' ) ) { 518 $current_lang = pll_current_language(); 519 $default_lang = pll_default_language(); 520 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 521 $original_pid = pll_get_post( $product_id, $default_lang ); 522 } 523 } 524 525 // Fallback if no original found 526 $opid_value = ( $original_pid && $original_pid !== $product_id ) ? $original_pid : $product_id; 527 528 // --- Patch API HTML --- 529 $nudges_section = $wiserrw_api_data->nudges_section ?? ''; 530 531 // Ensure pid 532 $nudges_section = preg_replace( '/data-pid=("|\')(.*?)\1/', 'data-pid="' . $product_id . '"', $nudges_section ); 533 534 // Ensure skuid 535 $nudges_section = preg_replace( '/data-skuid=("|\')(.*?)\1/', 'data-skuid="' . $sku . '"', $nudges_section ); 536 537 // Ensure opid 538 if ( preg_match( '/data-opid=/', $nudges_section ) ) { 539 $nudges_section = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $nudges_section ); 540 } else { 541 $nudges_section = preg_replace( '/<div\b/i', '<div data-opid="' . esc_attr( $opid_value ) . '"', $nudges_section, 1 ); 542 } 543 544 // Ensure lang 545 if ( ! empty( $current_lang ) ) { 546 if ( preg_match( '/data-lang=/', $nudges_section ) ) { 547 $nudges_section = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $nudges_section ); 548 } else { 549 $nudges_section = preg_replace( '/<div\b/i', '<div data-lang="' . esc_attr( $current_lang ) . '"', $nudges_section, 1 ); 550 } 551 } 552 553 // Output 554 echo $nudges_section; 555 339 556 return ob_get_clean(); 340 557 } … … 365 582 } 366 583 367 $api_endpoint = 'https://api.wiserreview.com/api/woocommerce/webhook?wsid=' . $wsid . '&atmid=' . $automation_id ;584 $api_endpoint = 'https://api.wiserreview.com/api/woocommerce/webhook?wsid=' . $wsid . '&atmid=' . $automation_id.'&ver='.WISERRW_PLUGIN_VERSION; 368 585 369 586 // Initialize arrays … … 393 610 } 394 611 395 $product_id = $item->get_product_id();612 $product_id = $item->get_product_id(); 396 613 $product_price = $product->get_price(); 397 $url = get_permalink($product_id); 398 $product_name = $item->get_name(); 399 614 $url = get_permalink($product_id); 615 $product_name = $item->get_name(); 616 617 // --- Multilingual handling (Added for opid/lang) --- 618 $original_pid = 0; 619 $current_lang = ''; 620 621 // WPML 622 if ( has_filter( 'wpml_element_trid' ) ) { 623 $trid = apply_filters( 'wpml_element_trid', null, $product_id, 'post_product' ); 624 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : []; 625 $current_lang = apply_filters( 'wpml_current_language', null ); 626 if ( $translations && is_array( $translations ) ) { 627 foreach ( $translations as $lang => $translation ) { 628 if ( ! empty( $translation->original ) ) { 629 $original_pid = (int) $translation->element_id; 630 } 631 } 632 } 633 } 634 635 // Polylang 636 if ( function_exists( 'pll_get_post' ) && function_exists( 'pll_current_language' ) ) { 637 $current_lang = pll_current_language(); 638 $default_lang = pll_default_language(); 639 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 640 $original_pid = pll_get_post( $product_id, $default_lang ); 641 } 642 } 643 644 // Fallback (if no translation plugin, opid = current product id) 645 $opid_value = ( $original_pid && $original_pid !== $product_id ) ? $original_pid : $product_id; 646 400 647 // Safely get product image 401 648 $image = wp_get_attachment_image_src(get_post_thumbnail_id($product_id), 'single-post-thumbnail'); … … 404 651 // Create new product data array for each item 405 652 $product_data = array( 406 'id' => $product_id, 407 'pn' => $product_name, 408 'name' => $product_name, 409 'prc' => $product_price, 410 'pu' => $url, 411 'piu' => $image_url 653 'id' => $product_id, 654 'pn' => $product_name, 655 'name' => $product_name, 656 'prc' => $product_price, 657 'pu' => $url, 658 'piu' => $image_url, 659 'opid' => $opid_value, // ✅ Added 660 'lang' => $current_lang ?: '' // ✅ Added 412 661 ); 413 662 … … 427 676 'httpversion' => '1.0', 428 677 'blocking' => true, 429 'body' => wp_json_encode($wiserrw_order_data), // Use wp_json_encode for better error handling678 'body' => wp_json_encode($wiserrw_order_data), 430 679 ) 431 680 ); … … 450 699 } 451 700 } 452 453 } 701 } 702 454 703 add_action( 'woocommerce_order_status_completed', 'wiserrw_woocommerce_order_status_completed' ); 455 704 … … 583 832 ); 584 833 } 834 585 835 add_action( 'wp_ajax_wiserrw_export_orders', 'wiserrw_export_orders' ); 586 836 … … 589 839 */ 590 840 function wiserrw_bulk_orders_send() { 591 check_ajax_referer( 'wiserrw_export_orders_nonce', 'wiserrw_security' ); 841 842 check_ajax_referer( 'wiserrw_export_orders_nonce', 'wiserrw_security' ); 592 843 593 844 if ( ! current_user_can( 'manage_woocommerce' ) ) { 594 wp_send_json_error( array( 'message' => 'You are not allowed to perform this action.' ), 403 ); 595 } 596 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 597 $api_key = $wiserrw_api_settings['wiserrw_api_key']; 598 $wsid = get_option( 'wiserrw_wsid', true ); 845 wp_send_json_error( 846 array( 'message' => __( 'You are not allowed to perform this action.', 'wiser-review' ) ), 847 403 848 ); 849 } 850 851 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 852 $api_key = $wiserrw_api_settings['wiserrw_api_key']; 853 $wsid = get_option( 'wiserrw_wsid', true ); 599 854 $automation_id = get_option( 'wiserrw_automation_id', true ); 600 if (!isset($wsid) || !isset($automation_id)) { 601 return; // Exit if invalid API data 602 } 603 $wiserrw_order_data = array(); 604 $wiserrw_line_items = array(); 605 $customer_data = array(); 606 $api_endpoint = 'https://api.wiserreview.com/api/woocommerce/webhook?wsid=' . $wsid . '&atmid=' . $automation_id; 607 608 $from_date = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : ''; 609 $to_date = isset( $_POST['end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['end_date'] ) ) : ''; 610 $duration = isset( $_POST['duration'] ) ? sanitize_text_field( wp_unslash( $_POST['duration'] ) ) : ''; 611 $offset = isset( $_POST['offset'] ) ? intval( sanitize_text_field( wp_unslash( $_POST['offset'] ) ) ) : ''; 612 $limit = isset( $_POST['limit'] ) ? intval( sanitize_text_field( wp_unslash( $_POST['limit'] ) ) ) : ''; 613 614 if ( '' !== $duration ) { 615 switch ( $duration ) { 616 case '30_days': 617 $date_from = strtotime( '-30 days' ); 618 break; 619 case '3_months': 620 $date_from = strtotime( '-3 months' ); 621 break; 622 case '6_months': 623 $date_from = strtotime( '-6 months' ); 624 break; 625 case '1_year': 626 $date_from = strtotime( '-1 year' ); 627 break; 628 case '7_days': 629 $date_from = strtotime( '-7 days' ); // Fixed: Now correctly gets 7 days of orders 630 break; 631 default: 632 $date_from = strtotime( '-7 days' ); 633 break; 634 } 635 636 // Get orders between date_from and now 637 $date_to = current_time('timestamp'); 638 $all_orders = wc_get_orders( 639 array( 640 'status' => array( 'completed', 'processing' ), 641 'date_created' => $date_from . '...' . $date_to, // Using WooCommerce's date range format 642 'limit' => -1, 643 ) 644 ); 645 } else { 646 $start_date = new WC_DateTime($from_date); 855 856 if ( !isset($wsid) || !isset($automation_id) ) { 857 return; // Exit if invalid API data 858 } 859 860 $wiserrw_order_data = array(); 861 $customer_data = array(); 862 $api_endpoint = 'https://api.wiserreview.com/api/woocommerce/webhook?wsid=' 863 . $wsid . '&atmid=' . $automation_id . '&ver=' . WISERRW_PLUGIN_VERSION; 864 865 $from_date = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : ''; 866 $to_date = isset( $_POST['end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['end_date'] ) ) : ''; 867 $duration = isset( $_POST['duration'] ) ? sanitize_text_field( wp_unslash( $_POST['duration'] ) ) : ''; 868 $offset = isset( $_POST['offset'] ) ? intval( sanitize_text_field( wp_unslash( $_POST['offset'] ) ) ) : ''; 869 $limit = isset( $_POST['limit'] ) ? intval( sanitize_text_field( wp_unslash( $_POST['limit'] ) ) ) : ''; 870 871 if ( '' !== $duration ) { 872 switch ( $duration ) { 873 case '30_days': $date_from = strtotime( '-30 days' ); break; 874 case '3_months': $date_from = strtotime( '-3 months' ); break; 875 case '6_months': $date_from = strtotime( '-6 months' ); break; 876 case '1_year': $date_from = strtotime( '-1 year' ); break; 877 case '7_days': $date_from = strtotime( '-7 days' ); break; 878 default: $date_from = strtotime( '-7 days' ); break; 879 } 880 881 $date_to = current_time('timestamp'); 882 $all_orders = wc_get_orders(array( 883 'status' => array( 'completed', 'processing' ), 884 'date_created' => $date_from . '...' . $date_to, 885 'limit' => -1, 886 )); 887 888 } else { 889 $start_date = new WC_DateTime($from_date); 647 890 $end_date = new WC_DateTime($to_date); 648 $all_orders = wc_get_orders( 649 array( 650 'status' => array( 'completed', 'processing' ), 651 'date_created' => gmdate( $start_date . '...' . $end_date ), 652 'limit' => -1, 653 ) 654 ); 655 } 656 657 $total = count( $all_orders ); 658 if ( $total === 0 ) { 659 wp_send_json_success([ 660 'percent' => 100, 661 'done' => true, 662 'message' => 'No orders found for selected range.', 663 ]); 891 $all_orders = wc_get_orders(array( 892 'status' => array( 'completed', 'processing' ), 893 'date_created' => $start_date->getTimestamp() . '...' . $end_date->getTimestamp(), 894 'limit' => -1, 895 )); 896 } 897 898 $total = count( $all_orders ); 899 if ( $total === 0 ) { 900 wp_send_json_success(array( 901 'percent' => 100, 902 'done' => true, 903 'message' => 'No orders found for selected range.', 904 )); 664 905 wp_die(); 665 906 } 666 907 667 if ( $total > 0 ) { 668 $chunk_ids = array_slice( $all_orders, $offset, $limit ); 669 $upload_dir = wp_upload_dir(); 670 $filename = 'orders-export-progress.csv'; 671 $filepath = $upload_dir['basedir'] . '/' . $filename; 672 $is_first_chunk = 0 === $offset; 673 674 foreach ( $chunk_ids as $order ) { 675 // Skip if this is a refund 676 if ($order instanceof WC_Order_Refund) { 677 continue; 678 } 679 $order_id = $order->get_id(); 680 681 682 /* Order Details */ 683 $wiserrw_order_data['oid'] = $order->get_id(); 684 $wiserrw_order_data['cdt'] = $order->get_date_created(); 685 $wiserrw_order_data['cdt'] = $order->get_date_created(); 686 687 /* Customer Details */ 688 $customer_data['email'] = $order->get_billing_email(); 689 $customer_data['phone'] = $order->get_billing_phone(); 690 $customer_data['first_name'] = $order->get_billing_first_name(); 691 $customer_data['last_name'] = $order->get_billing_last_name(); 692 $wiserrw_order_data['customer'] = $customer_data; 693 694 /* Product Details */ 695 $wiserrw_order_data['line_items'] = array(); // Clear line items for new order 696 $items = $order->get_items(); 697 foreach ( $items as $item ) { 698 $product = $item->get_product(); 699 if (!$product || !$product instanceof WC_Product) { 700 continue; // Skip if product doesn't exist anymore 701 } 702 $product_id = $item->get_product_id(); 703 $product_price = $product->get_price(); 704 $url = get_permalink($product_id); 705 $product_name = $item->get_name(); 706 $image = wp_get_attachment_image_src(get_post_thumbnail_id($product_id), 'single-post-thumbnail'); 707 $image_url = is_array($image) ? $image[0] : ''; 708 709 $product_data = array( // Initialize new array for each product 710 'id' => $product_id, 711 'pn' => $product_name, 712 'name' => $product_name, 713 'prc' => $product_price, 714 'pu' => $url, 715 'piu' => $image_url 716 ); 717 $wiserrw_order_data['line_items'][] = $product_data; 718 } 719 720 $response = wp_remote_post( 721 $api_endpoint, 722 array( 723 'method' => 'POST', 724 'headers' => array( 725 'Content-Type' => 'application/json', 726 ), 727 'timeout' => 45, 728 'redirection' => 5, 729 'httpversion' => '1.0', 730 'blocking' => true, 731 'body' => json_encode( $wiserrw_order_data ), 732 ) 733 ); 734 if ( is_wp_error( $response ) ) { 735 $error_message = $response->get_error_message(); 736 error_log(sprintf( 737 'WiserReview Bulk Send API Error (Order #%d): %s', 738 $order_id, 739 $error_message 740 )); 741 $api_error_messages[] = $error_message; 742 } else { 743 $response_code = wp_remote_retrieve_response_code($response); 744 if ($response_code !== 200) { 745 $error_message = wp_remote_retrieve_body($response); 746 error_log(sprintf( 747 'WiserReview Bulk Send API Non-200 Response (Order #%d): Code %d - %s', 748 $order_id, 749 $response_code, 750 $error_message 751 )); 752 $api_error_messages[] = $error_message; 753 } 754 } 755 } 756 757 $processed = $offset + count( $chunk_ids ); 758 $percent = min(round( ( $processed / $total ) * 100 ), 100); // Ensure we don't exceed 100% 759 $is_done = $processed >= $total; 760 761 $response_data = array( 762 'percent' => $percent, 763 'done' => $is_done, 764 'total_processed' => $is_done ? $total : $processed, 765 'total_orders' => $total, 766 'processed_chunk' => count($chunk_ids), 767 'api_error_messages' => $api_error_messages 768 ); 769 770 wp_send_json_success($response_data); 771 } 772 die(); 773 } 908 if ( $total > 0 ) { 909 $chunk_ids = array_slice( $all_orders, $offset, $limit ); 910 $api_errors = array(); 911 912 foreach ( $chunk_ids as $order ) { 913 if ( $order instanceof WC_Order_Refund ) { 914 continue; // skip refunds 915 } 916 917 $order_id = $order->get_id(); 918 919 /* Order Details */ 920 $wiserrw_order_data['oid'] = $order_id; 921 $wiserrw_order_data['cdt'] = $order->get_date_created() 922 ? $order->get_date_created()->format('Y-m-d H:i:s') // ✅ Clean date format 923 : current_time('mysql'); 924 925 /* Customer Details */ 926 $customer_data = array( 927 'email' => $order->get_billing_email(), 928 'phone' => $order->get_billing_phone(), 929 'first_name' => $order->get_billing_first_name(), 930 'last_name' => $order->get_billing_last_name(), 931 ); 932 $wiserrw_order_data['customer'] = $customer_data; 933 934 /* Product Details */ 935 $wiserrw_order_data['line_items'] = array(); 936 foreach ( $order->get_items() as $item ) { 937 $product = $item->get_product(); 938 if ( !$product || !$product instanceof WC_Product ) { 939 continue; 940 } 941 942 $product_id = $item->get_product_id(); 943 $product_name = $item->get_name(); 944 $product_price = $product->get_price(); 945 $url = get_permalink($product_id); 946 $image = wp_get_attachment_image_src(get_post_thumbnail_id($product_id), 'single-post-thumbnail'); 947 $image_url = is_array($image) ? $image[0] : ''; 948 949 // --- Multilingual handling --- 950 $original_pid = 0; 951 $current_lang = ''; 952 953 // WPML 954 if ( has_filter( 'wpml_element_trid' ) ) { 955 $trid = apply_filters( 'wpml_element_trid', null, $product_id, 'post_product' ); 956 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : []; 957 $current_lang = apply_filters( 'wpml_current_language', null ); 958 if ( $translations && is_array($translations) ) { 959 foreach ( $translations as $lang => $translation ) { 960 if ( ! empty( $translation->original ) ) { 961 $original_pid = (int) $translation->element_id; 962 } 963 } 964 } 965 } 966 967 // Polylang 968 if ( function_exists( 'pll_get_post' ) && function_exists( 'pll_current_language' ) ) { 969 $current_lang = pll_current_language(); 970 $default_lang = pll_default_language(); 971 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 972 $original_pid = pll_get_post( $product_id, $default_lang ); 973 } 974 } 975 976 // Fallback 977 $opid_value = ( $original_pid && $original_pid !== $product_id ) ? $original_pid : $product_id; 978 979 $product_data = array( 980 'id' => $product_id, 981 'pn' => $product_name, 982 'name' => $product_name, 983 'prc' => $product_price, 984 'pu' => $url, 985 'piu' => $image_url, 986 'opid' => $opid_value, // ✅ Added 987 'lang' => $current_lang ?: '' // ✅ Added 988 ); 989 990 $wiserrw_order_data['line_items'][] = $product_data; 991 } 992 993 // Send to API 994 $response = wp_remote_post($api_endpoint, array( 995 'method' => 'POST', 996 'headers' => array('Content-Type' => 'application/json'), 997 'timeout' => 45, 998 'blocking' => true, 999 'body' => wp_json_encode($wiserrw_order_data), 1000 )); 1001 1002 if ( is_wp_error($response) ) { 1003 $api_errors[] = $response->get_error_message(); 1004 } else { 1005 $code = wp_remote_retrieve_response_code($response); 1006 if ( $code !== 200 ) { 1007 $api_errors[] = wp_remote_retrieve_body($response); 1008 } 1009 } 1010 } 1011 1012 $processed = $offset + count( $chunk_ids ); 1013 $percent = min(round( ( $processed / $total ) * 100 ), 100); 1014 $is_done = $processed >= $total; 1015 1016 wp_send_json_success(array( 1017 'percent' => $percent, 1018 'done' => $is_done, 1019 'total_processed' => $is_done ? $total : $processed, 1020 'total_orders' => $total, 1021 'processed_chunk' => count($chunk_ids), 1022 'api_error_messages' => $api_errors 1023 )); 1024 } 1025 1026 die(); 1027 } 1028 774 1029 add_action( 'wp_ajax_wiserrw_bulk_orders_send', 'wiserrw_bulk_orders_send' ); 775 1030 … … 780 1035 check_ajax_referer( 'wiserrw_export_orders_nonce', 'wiserrw_security' ); 781 1036 if ( ! current_user_can( 'manage_woocommerce' ) ) { 782 wp_send_json_error( array( 'message' => 'You are not allowed to perform this action.'), 403 );1037 wp_send_json_error( array( 'message' => __('You are not allowed to perform this action.', 'wiser-review' ) ), 403 ); 783 1038 } 784 1039 $duration = isset( $_POST['duration'] ) ? sanitize_text_field( wp_unslash( $_POST['duration'] ) ) : ''; … … 866 1121 function wiserrw_sync_products() { 867 1122 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 868 $full_product_synced = get_option( 'wiserrw_all_products_synced' );869 1123 $api_host = constant( 'WISERRW_API_HOST' ); 870 1124 $per_page = isset( $_REQUEST['per_page'] ) ? intval( $_REQUEST['per_page'] ) : 50; … … 877 1131 } 878 1132 1133 // Pagination setup 879 1134 if ( isset( $_REQUEST['total_pages'] ) ) { 880 1135 $total_pages = (int) sanitize_text_field( wp_unslash( $_REQUEST['total_pages'] ) ); … … 890 1145 $message = "Synced $page / $total_pages pages"; 891 1146 $products = wiserrw_get_products( $page, $per_page ); 892 $products_json = array();1147 $products_json = []; 893 1148 894 1149 foreach ( $products as $prod ) { … … 899 1154 } 900 1155 901 $arrsku = array(); 902 $pr = wc_get_product( $p_id ); 1156 $pr = wc_get_product( $p_id ); 903 1157 if ( ! $pr ) { 904 1158 continue; 905 1159 } 906 1160 907 // Collect SKUs similar to your original 1161 // Collect SKUs 1162 $arrsku = []; 908 1163 if ( $pr->is_type( 'variable' ) ) { 909 1164 $prod_variation = new WC_Product_Variable( $p_id ); … … 921 1176 } 922 1177 923 // Representative barcode (same idea you used)1178 // Representative barcode 924 1179 $gtin = get_post_meta( $p_id, 'hwp_product_gtin', true ) 925 1180 ?: $pr->get_attribute( 'GTIN' ) … … 928 1183 ?: get_post_meta( $p_id, '_global_unique_id', true ); 929 1184 930 // Title and URL fallbacks1185 // Title and URL 931 1186 $title = $prod['title']; 932 1187 if ( empty( $title ) ) { … … 946 1201 } 947 1202 948 // Image URL with placeholder fallback1203 // Image URL 949 1204 $img_src = wp_get_attachment_image_src( get_post_thumbnail_id( $p_id ), 'shop_single' ); 950 1205 $img_url = ( is_array( $img_src ) && isset( $img_src[0] ) ) ? $img_src[0] : wc_placeholder_img_src( 'shop_single' ); 951 1206 952 $product_json = array( 953 'pid' => $p_id, 954 'arrsku' => $arrsku, 955 'shp' => get_site_url(), 956 'wsid' => $wsid, 957 'pn' => $title, 958 'piu' => esc_url( $img_url ), 959 'pu' => esc_url( $product_url ), 960 'barcode' => (string) $gtin, 961 ); 1207 // --- Multilingual handling (product-specific) --- 1208 $original_pid = 0; 1209 $current_lang = ''; 1210 1211 // WPML 1212 if ( has_filter( 'wpml_element_trid' ) ) { 1213 $trid = apply_filters( 'wpml_element_trid', null, $p_id, 'post_product' ); 1214 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : []; 1215 if ( $translations && is_array( $translations ) ) { 1216 foreach ( $translations as $lang => $translation ) { 1217 if ( ! empty( $translation->original ) ) { 1218 $original_pid = (int) $translation->element_id; 1219 } 1220 if ( (int) $translation->element_id === $p_id ) { 1221 $current_lang = $lang; // ✅ product-specific language 1222 } 1223 } 1224 } 1225 } 1226 1227 // Polylang 1228 if ( function_exists( 'pll_get_post' ) && function_exists( 'pll_get_post_language' ) ) { 1229 $current_lang = pll_get_post_language( $p_id ); // ✅ product-specific language 1230 $default_lang = function_exists( 'pll_default_language' ) ? pll_default_language() : ''; 1231 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 1232 $original_pid = pll_get_post( $p_id, $default_lang ); 1233 } 1234 } 1235 1236 // Fallback 1237 $opid_value = ( $original_pid && $original_pid !== $p_id ) ? $original_pid : $p_id; 1238 1239 // Build product payload 1240 $product_json = [ 1241 'pid' => $p_id, 1242 'arrsku' => $arrsku, 1243 'shp' => get_site_url(), 1244 'wsid' => $wsid, 1245 'pn' => $title, 1246 'piu' => esc_url( $img_url ), 1247 'pu' => esc_url( $product_url ), 1248 'barcode' => (string) $gtin, 1249 'opid' => $opid_value, // ✅ Added 1250 'lang' => $current_lang ?: '' // ✅ Fixed: per-product lang 1251 ]; 962 1252 963 1253 $products_json[] = $product_json; 964 1254 965 // Mark as registered (like your original)1255 // Mark as registered 966 1256 update_post_meta( $p_id, '_wiserrw_product_registered', '1' ); 967 1257 } 968 1258 969 1259 if ( ! empty( $products_json ) ) { 970 $data = array(1260 $data = [ 971 1261 'method' => 'POST', 972 1262 'blocking' => true, 973 'headers' => array( 'Content-Type' => 'application/json' ), 974 'body' => wp_json_encode( array( 'products' => $products_json ) ), 975 ); 1263 'headers' => [ 'Content-Type' => 'application/json' ], 1264 'body' => wp_json_encode( [ 1265 'products' => $products_json, 1266 'ver' => WISERRW_PLUGIN_VERSION // ✅ Added once at root level 1267 ] ), 1268 ]; 976 1269 $url = $api_host . 'productWebhook?wsid=' . $wsid . '&key=' . $api_key; 977 1270 $response = wp_safe_remote_post( $url, $data ); 978 979 // (Silent on errors, like your original; you can log if you want)980 1271 } 981 1272 } else { … … 989 1280 990 1281 $page++; 991 $result = array(1282 $result = [ 992 1283 'next_page' => $page, 993 1284 'total_pages' => $total_pages, 994 1285 'message' => $message, 995 1286 'finished' => $finished, 996 );1287 ]; 997 1288 998 1289 echo wp_json_encode( $result ); … … 1000 1291 } 1001 1292 1293 1002 1294 add_action( 'wp_ajax_wiserrw_sync_products', 'wiserrw_sync_products' ); 1003 1295 add_action( 'wp_ajax_nopriv_wiserrw_sync_products', 'wiserrw_sync_products' ); … … 1009 1301 check_ajax_referer( 'wiserrw_export_orders_nonce', 'wiserrw_security' ); 1010 1302 if ( ! current_user_can( 'manage_woocommerce' ) ) { 1011 wp_send_json_error( array( 'message' => 'You are not allowed to perform this action.'), 403 );1303 wp_send_json_error( array( 'message' => __('You are not allowed to perform this action.','wiser-review' ) ), 403 ); 1012 1304 } 1013 1305 // Clear only the "registered" flag for all products … … 1021 1313 // JSON response (unchanged style) 1022 1314 $result = array( 1023 'message' => 'All Products Sync Status Cleared',1315 'message' => __( 'All Products Sync Status Cleared', 'wiser-review' ), 1024 1316 ); 1025 1317 echo wp_json_encode( $result ); … … 1061 1353 add_action( 'woocommerce_after_product_object_save', 'wiserrw_product_update_hook', 99, 1 ); 1062 1354 1063 1064 1355 function wiserrw_product_update_hook( $product ) { 1065 if ( ! $product instanceof WC_Product ) { 1066 return; 1067 } 1068 1069 $p_id = $product->get_id(); 1070 1071 // Skip autosaves / revisions 1072 if ( wp_is_post_autosave( $p_id ) || wp_is_post_revision( $p_id ) ) { 1073 return; 1074 } 1075 1076 // Skip background cron or ajax jobs (vendor imports often run this way) 1077 if ( ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { 1078 return; 1079 } 1080 1081 $is_variation = $product instanceof WC_Product_Variation; 1082 $parent_id = $is_variation ? $product->get_parent_id() : $p_id; 1083 1084 // --- Watched fields --- 1085 1086 // Name 1087 $name = (string) $product->get_name(); 1088 1089 // Permalink (normalize: strip trailing slash) 1090 $permalink = untrailingslashit( (string) get_permalink( $is_variation ? $parent_id : $p_id ) ); 1091 1092 // Image ID (normalize to string "0" if none) 1093 $effective_image_id = (int) $product->get_image_id(); 1094 if ( ! $effective_image_id ) { 1095 $effective_image_id = (int) get_post_thumbnail_id( $is_variation ? $parent_id : $p_id ); 1096 } 1097 $image_id = (string) ( $effective_image_id ?: 0 ); 1098 1099 // SKU fingerprint 1100 if ( $product->is_type( 'variable' ) && ! $is_variation ) { 1101 $child_skus = array(); 1102 foreach ( $product->get_children() as $vid ) { 1103 $vsku = strtolower( trim( (string) get_post_meta( $vid, '_sku', true ) ) ); 1104 if ( $vsku !== '' ) { 1105 $child_skus[] = $vsku; 1106 } 1107 } 1108 sort( $child_skus, SORT_NATURAL | SORT_FLAG_CASE ); 1109 $sku_fingerprint = implode( ',', $child_skus ); 1110 } else { 1111 $sku_fingerprint = strtolower( trim( (string) $product->get_sku() ) ); 1112 } 1113 1114 // Barcode / GTIN (normalize) 1115 if ( $is_variation ) { 1116 $barcode = get_post_meta( $p_id, 'hwp_var_gtin', true ) 1117 ?: get_post_meta( $parent_id, 'hwp_product_gtin', true ) 1118 ?: $product->get_attribute( 'GTIN' ) 1119 ?: $product->get_attribute( 'EAN' ) 1120 ?: $product->get_attribute( 'ISBN' ) 1121 ?: get_post_meta( $p_id, '_global_unique_id', true ); 1122 } else { 1123 $barcode = get_post_meta( $p_id, 'hwp_product_gtin', true ) 1124 ?: $product->get_attribute( 'GTIN' ) 1125 ?: $product->get_attribute( 'EAN' ) 1126 ?: $product->get_attribute( 'ISBN' ) 1127 ?: get_post_meta( $p_id, '_global_unique_id', true ); 1128 } 1129 $barcode = strtolower( trim( (string) $barcode ) ); 1130 1131 // Build watch hash 1132 $new_hash = md5( $name . '|' . $permalink . '|' . $image_id . '|' . $sku_fingerprint . '|' . $barcode ); 1133 $old_hash = (string) get_post_meta( $p_id, '_wiserrw_watch_hash', true ); 1134 1135 // If nothing changed, exit 1136 if ( $new_hash === $old_hash ) { 1137 return; 1138 } 1139 1140 // --- Prepare payload --- 1141 1142 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 1143 if ( empty( $wiserrw_api_settings['wiserrw_api_key'] ) ) { 1144 return; 1145 } 1146 $api_host = constant( 'WISERRW_API_HOST' ); 1147 $api_key = $wiserrw_api_settings['wiserrw_api_key']; 1148 $wsid = get_option( 'wiserrw_wsid', true ); 1149 $automation_id = get_option( 'wiserrw_automation_id', true ); 1150 if ( ! $wsid || ! $automation_id ) { 1151 return; 1152 } 1153 1154 // Build arrsku 1155 $arrsku = array(); 1156 if ( $product->is_type( 'variable' ) && ! $is_variation ) { 1157 foreach ( $product->get_children() as $vid ) { 1158 $vsku = strtolower( trim( (string) get_post_meta( $vid, '_sku', true ) ) ); 1159 if ( $vsku !== '' ) { 1160 $arrsku[] = $vsku; 1161 } 1162 } 1163 } else { 1164 if ( $sku_fingerprint !== '' ) { 1165 $arrsku[] = $sku_fingerprint; 1166 } 1167 } 1168 1169 // Image URL (fallback to placeholder) 1170 $thumb_id = $effective_image_id; 1171 $img_src = $thumb_id ? wp_get_attachment_image_src( $thumb_id, 'shop_single' ) : false; 1172 $img_url = ( is_array( $img_src ) && isset( $img_src[0] ) ) ? $img_src[0] : wc_placeholder_img_src( 'shop_single' ); 1173 1174 $product_json = array( 1175 'pid' => (string) $p_id, 1176 'arrsku' => $arrsku, 1177 'shp' => get_site_url(), 1178 'wsid' => $wsid, 1179 'pn' => $name ?: get_the_title( $parent_id ), 1180 'piu' => $img_url, 1181 'pu' => $permalink, 1182 'barcode' => $barcode, 1183 ); 1184 1185 $args = array( 1186 'method' => 'POST', 1187 'blocking' => true, 1188 'headers' => array( 'Content-Type' => 'application/json' ), 1189 'body' => wp_json_encode( array( 'products' => array( $product_json ) ) ), 1190 ); 1191 1192 // Clear flag before sending 1193 delete_post_meta( $p_id, '_wiserrw_product_registered' ); 1194 1195 $response = wp_safe_remote_post( $api_host . 'productWebhook?wsid=' . $wsid . '&key=' . $api_key, $args ); 1196 if ( is_wp_error( $response ) ) { 1197 return; 1198 } 1199 1200 if ( (int) wp_remote_retrieve_response_code( $response ) === 200 ) { 1201 update_post_meta( $p_id, '_wiserrw_product_registered', '1' ); 1202 update_post_meta( $p_id, '_wiserrw_watch_hash', $new_hash ); 1203 } 1204 } 1205 1206 1207 // -------------------- Custom REST API Endpoints -------------------- // 1356 if ( ! $product instanceof WC_Product ) { 1357 return; 1358 } 1359 1360 $p_id = $product->get_id(); 1361 1362 // Skip autosaves / revisions 1363 if ( wp_is_post_autosave( $p_id ) || wp_is_post_revision( $p_id ) ) { 1364 return; 1365 } 1366 1367 $is_variation = $product instanceof WC_Product_Variation; 1368 $parent_id = $is_variation ? $product->get_parent_id() : $p_id; 1369 1370 // --- Current values we watch --- 1371 $name = (string) $product->get_name(); 1372 $permalink = (string) get_permalink( $is_variation ? $parent_id : $p_id ); 1373 1374 // Effective image id (variation image, else parent/simple image) 1375 $effective_image_id = (int) $product->get_image_id(); 1376 if ( ! $effective_image_id ) { 1377 $effective_image_id = (int) get_post_thumbnail_id( $is_variation ? $parent_id : $p_id ); 1378 } 1379 $image_id = (string) $effective_image_id; 1380 1381 // SKU fingerprint 1382 if ( $product->is_type( 'variable' ) && ! $is_variation ) { 1383 $child_skus = array(); 1384 foreach ( $product->get_children() as $vid ) { 1385 $vsku = (string) get_post_meta( $vid, '_sku', true ); 1386 if ( $vsku !== '' ) { 1387 $child_skus[] = $vsku; 1388 } 1389 } 1390 sort( $child_skus, SORT_NATURAL | SORT_FLAG_CASE ); 1391 $sku_fingerprint = implode( ',', $child_skus ); 1392 } else { 1393 $sku_fingerprint = (string) $product->get_sku(); 1394 } 1395 1396 // Barcode 1397 if ( $is_variation ) { 1398 $barcode = get_post_meta( $p_id, 'hwp_var_gtin', true ) 1399 ?: get_post_meta( $parent_id, 'hwp_product_gtin', true ) 1400 ?: $product->get_attribute( 'GTIN' ) 1401 ?: $product->get_attribute( 'EAN' ) 1402 ?: $product->get_attribute( 'ISBN' ) 1403 ?: get_post_meta( $p_id, '_global_unique_id', true ); 1404 } else { 1405 $barcode = get_post_meta( $p_id, 'hwp_product_gtin', true ) 1406 ?: $product->get_attribute( 'GTIN' ) 1407 ?: $product->get_attribute( 'EAN' ) 1408 ?: $product->get_attribute( 'ISBN' ) 1409 ?: get_post_meta( $p_id, '_global_unique_id', true ); 1410 } 1411 $barcode = (string) $barcode; 1412 1413 // Watch hash 1414 $new_hash = md5( $name . '|' . $permalink . '|' . $image_id . '|' . $sku_fingerprint . '|' . $barcode ); 1415 $old_hash = (string) get_post_meta( $p_id, '_wiserrw_watch_hash', true ); 1416 if ( $new_hash === $old_hash ) { 1417 return; 1418 } 1419 1420 // --- API settings --- 1421 $wiserrw_api_settings = get_option( 'wiserrw_api_settings' ); 1422 if ( empty( $wiserrw_api_settings['wiserrw_api_key'] ) ) { 1423 return; 1424 } 1425 $api_host = constant( 'WISERRW_API_HOST' ); 1426 $api_key = $wiserrw_api_settings['wiserrw_api_key']; 1427 $wsid = get_option( 'wiserrw_wsid', true ); 1428 $automation_id = get_option( 'wiserrw_automation_id', true ); 1429 if ( ! $wsid || ! $automation_id ) { 1430 return; 1431 } 1432 1433 // arrsku 1434 $arrsku = array(); 1435 if ( $product->is_type( 'variable' ) && ! $is_variation ) { 1436 foreach ( $product->get_children() as $vid ) { 1437 $vsku = (string) get_post_meta( $vid, '_sku', true ); 1438 if ( $vsku !== '' ) { 1439 $arrsku[] = $vsku; 1440 } 1441 } 1442 } else { 1443 if ( $sku_fingerprint !== '' ) { 1444 $arrsku[] = $sku_fingerprint; 1445 } 1446 } 1447 1448 // Image URL 1449 $thumb_id = $effective_image_id; 1450 $img_src = $thumb_id ? wp_get_attachment_image_src( $thumb_id, 'shop_single' ) : false; 1451 $img_url = ( is_array( $img_src ) && isset( $img_src[0] ) ) ? $img_src[0] : wc_placeholder_img_src( 'shop_single' ); 1452 1453 // --- Multilingual handling (Added) --- 1454 $original_pid = 0; 1455 $current_lang = ''; 1456 1457 // WPML 1458 if ( has_filter( 'wpml_element_trid' ) ) { 1459 $trid = apply_filters( 'wpml_element_trid', null, $p_id, 'post_product' ); 1460 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : array(); 1461 if ( $translations && is_array( $translations ) ) { 1462 foreach ( $translations as $lang => $translation ) { 1463 if ( ! empty( $translation->original ) ) { 1464 $original_pid = (int) $translation->element_id; 1465 } 1466 if ( (int) $translation->element_id === $p_id ) { 1467 $current_lang = $lang; 1468 } 1469 } 1470 } 1471 } 1472 1473 // Polylang 1474 if ( function_exists( 'pll_get_post_language' ) ) { 1475 $current_lang = pll_get_post_language( $p_id ); 1476 if ( function_exists( 'pll_default_language' ) ) { 1477 $default_lang = pll_default_language(); 1478 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 1479 $original_pid = pll_get_post( $p_id, $default_lang ); 1480 } 1481 } 1482 } 1483 1484 // Fallbacks 1485 $opid_value = ( $original_pid && $original_pid !== $p_id ) ? $original_pid : $p_id; 1486 1487 // --- Build product payload --- 1488 $product_json = array( 1489 'pid' => (string) $p_id, 1490 'arrsku' => $arrsku, 1491 'shp' => get_site_url(), 1492 'wsid' => $wsid, 1493 'pn' => $name ?: get_the_title( $parent_id ), 1494 'piu' => $img_url, 1495 'pu' => $permalink, 1496 'barcode' => $barcode, 1497 'opid' => $opid_value, // ✅ Added 1498 'lang' => $current_lang ?: '' // ✅ Added 1499 ); 1500 1501 $args = array( 1502 'method' => 'POST', 1503 'blocking' => true, 1504 'headers' => array( 'Content-Type' => 'application/json' ), 1505 'body' => wp_json_encode( array( 1506 'products' => array( $product_json ), 1507 'ver' => WISERRW_PLUGIN_VERSION 1508 ) ), 1509 ); 1510 1511 // Clear flag before send 1512 delete_post_meta( $p_id, '_wiserrw_product_registered' ); 1513 1514 $response = wp_safe_remote_post( $api_host . 'productWebhook?wsid=' . $wsid . '&key=' . $api_key, $args ); 1515 if ( is_wp_error( $response ) ) { 1516 return; 1517 } 1518 1519 if ( (int) wp_remote_retrieve_response_code( $response ) === 200 ) { 1520 update_post_meta( $p_id, '_wiserrw_product_registered', '1' ); 1521 update_post_meta( $p_id, '_wiserrw_watch_hash', $new_hash ); 1522 } 1523 } 1524 1525 1208 1526 1209 1527 function wiserrw_custom_endpoints() { … … 1264 1582 1265 1583 1584 // === MAIN API ENDPOINT (returns instantly) === 1266 1585 function wiserrw_api_callback( WP_REST_Request $request ) { 1267 1586 1268 1587 $payload = array( 1269 'status' => 'ok', 1270 'method' => $request->get_method(), 1271 'time' => current_time( 'mysql', true ), 1272 'data' => $request->get_json_params() ?: $request->get_params(), 1273 ); 1274 do_action( 'wiserrw_endpoint_called', $request ); 1275 return new WP_REST_Response( $payload, 200 ); 1276 } 1277 1278 1588 'status' => 'ok', 1589 'method' => $request->get_method(), 1590 'time' => current_time( 'mysql', true ), 1591 'data' => $request->get_json_params() ?: $request->get_params(), 1592 ); 1593 1594 // Fire background call to self (non-blocking) 1595 wp_remote_post( 1596 rest_url( 'wiserreview/v1/async' ), 1597 array( 1598 'blocking' => false, 1599 'timeout' => 3, 1600 'body' => array( 1601 'json_data' => wp_json_encode( $payload ), 1602 ), 1603 ) 1604 ); 1605 1606 // Return immediately 1607 return new WP_REST_Response( $payload, 200 ); 1608 } 1609 add_action( 'rest_api_init', function() { 1610 register_rest_route( 'wiserreview/v1', '/api', array( 1611 'methods' => 'POST', 1612 'callback' => 'wiserrw_api_callback', 1613 'permission_callback' => '__return_true', 1614 )); 1615 }); 1616 1617 // === ASYNC BACKGROUND HANDLER === 1618 add_action( 'rest_api_init', function() { 1619 register_rest_route( 'wiserreview/v1', '/async', array( 1620 'methods' => 'POST', 1621 'callback' => 'wiserrw_async_handler', 1622 'permission_callback' => '__return_true', 1623 )); 1624 }); 1625 1626 function wiserrw_async_handler( WP_REST_Request $request ) { 1627 1628 $json_data = $request->get_param( 'json_data' ); 1629 if ( empty( $json_data ) ) { 1630 return new WP_REST_Response( array( 'error' => 'No data received' ), 400 ); 1631 } 1632 1633 $payload = json_decode( $json_data, true ); 1634 if ( empty( $payload['data'] ) || ! is_array( $payload['data'] ) ) { 1635 return new WP_REST_Response( array( 'error' => 'Invalid payload' ), 400 ); 1636 } 1637 1638 // Build a fake REST request to reuse your existing logic 1639 $fake_request = new WP_REST_Request( 'POST', '/wiserreview/v1/api' ); 1640 1641 foreach ( $payload['data'] as $key => $value ) { 1642 $fake_request->set_param( $key, $value ); 1643 } 1644 1645 // Force JSON body so get_json_params() works inside callback 1646 $fake_request->set_body( wp_json_encode( $payload['data'] ) ); 1647 $fake_request->set_header( 'Content-Type', 'application/json' ); 1648 1649 // Run your main process in background 1650 do_action( 'wiserrw_endpoint_called', $fake_request ); 1651 1652 return new WP_REST_Response( array( 'status' => 'background task started' ), 200 ); 1653 } 1654 1655 1656 // === MAIN LOGIC HANDLER === 1279 1657 function wiserrw_api_response_callback( $request ) { 1280 $data = $request->get_json_params() ?: $request->get_params(); 1281 1282 $query_params = $request->get_params(); 1283 if (empty($data) || !is_array($data)) { 1658 1659 // --- Safely parse JSON and normal params --- 1660 $raw = $request->get_body(); 1661 $data = json_decode( $raw, true ); 1662 1663 // Fallback if JSON body not parsed 1664 if ( empty( $data ) || ! is_array( $data ) ) { 1665 $data = $request->get_json_params() ?: $request->get_params(); 1666 } 1667 1668 if ( empty( $data ) || ! is_array( $data ) ) { 1284 1669 return; 1285 1670 } 1286 $verify = isset($query_params['verify_api']) ? $query_params['verify_api'] : ''; 1287 $wsid_param = isset($query_params['wiserrw_wsid']) ? $query_params['wiserrw_wsid'] : ''; 1671 // ✅ CHANGED: read everything from body, not from query 1672 $wsid_param = $data['wsid'] ?? ''; 1673 $verify_api = $data['verifyApi'] ?? ''; 1674 $updtyp = $data['updtyp'] ?? ''; 1675 $widget_id = $data['widgetId'] ?? ''; 1676 1677 // Current workspace ID saved in site 1288 1678 $wsid = get_option( 'wiserrw_wsid', '' ); 1289 // Verify API key if 'verify_api' is true and 'wiserrw_wsid' matches 1290 if ( $verify && $wsid !== '' && $wsid == $wsid_param ) { 1291 $wiserrw_api_settings = get_option('wiserrw_api_settings', array()); 1292 $api_key = $wiserrw_api_settings['wiserrw_api_key']; 1293 wiserrw_validate_api( $api_key ); 1294 } 1295 1296 1297 if (empty($data) || !is_array($data)) { 1298 return; 1299 } 1300 1301 update_option( 'wiserrw_response', $data ); 1302 1303 if (!isset($data['arrPid']) || !is_array($data['arrPid'])) { 1304 return; 1305 } 1306 1307 foreach( $data['arrPid'] as $p_id ) { 1308 if (!empty($p_id) && is_numeric($p_id)) { 1309 wiserrw_generate_schema( $p_id ); 1310 } 1679 1680 switch ( $updtyp ) { 1681 1682 // --- CSS update --- 1683 case 'update_css': 1684 // ✅ CHANGED: verifyApi now read from body (string "true") 1685 $is_verified = filter_var( $verify_api, FILTER_VALIDATE_BOOLEAN ); 1686 1687 if ( $is_verified && $wsid !== '' && $wsid === $wsid_param ) { 1688 $wiserrw_api_settings = get_option( 'wiserrw_api_settings', array() ); 1689 $api_key = $wiserrw_api_settings['wiserrw_api_key'] ?? ''; 1690 1691 // Validate API and update CSS 1692 wiserrw_validate_api( $api_key ); 1693 wiserrw_update_css( $wsid ); 1694 } 1695 break; 1696 1697 // --- HTML update (clear widget cache) --- 1698 case 'update_html': 1699 if ( ! empty( $widget_id ) ) { 1700 $option_key = 'wiserreview_widget_' . $widget_id; 1701 delete_option( $option_key ); 1702 } 1703 break; 1704 1705 // --- Schema generation and async remote update --- 1706 case 'update_schema': 1707 if ( ! empty( $data['arrPid'] ) && is_array( $data['arrPid'] ) ) { 1708 $wsid_to_use = $data['wsid'] ?? $wsid; 1709 1710 foreach ( $data['arrPid'] as $p_id ) { 1711 if ( ! empty( $p_id ) && is_numeric( $p_id ) ) { 1712 1713 // Run schema generation now (needs WP context) 1714 wiserrw_generate_schema( $p_id ); 1715 1716 // Send async schema update (non-blocking) 1717 wp_remote_post( 1718 'https://rs.wiserreview.com/api/wp/updSchema', 1719 array( 1720 'method' => 'POST', 1721 'headers' => array( 'Content-Type' => 'application/json' ), 1722 'body' => wp_json_encode( array( 1723 'wsid' => $wsid_to_use, 1724 'pid' => $p_id, 1725 'updtyp' => 'update_schema', 1726 'ver' => WISERRW_PLUGIN_VERSION, 1727 )), 1728 'blocking' => false, 1729 'timeout' => 3, 1730 ) 1731 ); 1732 } 1733 } 1734 } 1735 break; 1736 1737 default: 1738 // No action for unknown updtyp 1739 break; 1311 1740 } 1312 1741 } 1313 1742 add_action( 'wiserrw_endpoint_called', 'wiserrw_api_response_callback' ); 1314 1743 1315 function wiserrw_generate_schema( $p_id ) { 1316 if (empty($p_id) || !is_numeric($p_id)) { 1317 return; 1318 } 1319 1320 $wiserrw_api_settings = get_option( 'wiserrw_api_settings', array() ); 1321 if (!is_array($wiserrw_api_settings) || empty($wiserrw_api_settings['wiserrw_api_key'])) { 1322 return; 1323 } 1324 1325 $api_key = $wiserrw_api_settings['wiserrw_api_key']; 1326 $wsid = get_option( 'wiserrw_wsid', true ); 1327 $automation_id = get_option( 'wiserrw_automation_id', true ); 1328 if (!isset($wsid) || !isset($automation_id)) { 1329 return; // Exit if invalid API data 1330 } 1331 1332 $p_id_arr = array($p_id); // Initialize array properly 1333 $url = 'https://rs.wiserreview.com/api/getData'; 1334 1335 1336 for( $i=0; $i<count($p_id_arr); $i++ ){ 1337 $product = get_post($p_id_arr[$i]); 1338 if( !$product ) { 1339 continue; 1340 } 1341 // ✅ Reset on every loop 1342 $arrPid = array( (string) $p_id_arr[$i] ); 1343 $schema_array = array(); 1344 $body = array( 1345 'arrType' => array('main','star_rating'), 1346 'pid' => (string)$p_id_arr[$i], 1347 'arrPid' => $arrPid, 1348 'isFrom' => 'woocommerce', 1349 'wsid' => $wsid, 1350 ); 1351 1352 $response = wp_remote_post( 1353 $url, 1354 array( 1355 'method' => 'POST', 1356 'headers' => array( 1357 'Content-Type' => 'application/json', 1358 ), 1359 'body' => wp_json_encode( $body ), 1360 'timeout' => 30, 1361 ) 1362 ); 1363 if ( !is_wp_error( $response ) ) { 1364 $json = wp_remote_retrieve_body( $response ); 1365 $data = json_decode( $json, true ); 1366 foreach( $data['data'] as $row ) { 1367 $wdtyp = $row['wdtyp']; 1368 if( $wdtyp == 'main' ){ 1369 1370 $schema_array['@context'] = "https://schema.org"; 1371 $schema_array['@type'] = "Product"; 1372 $schema_array['url'] = get_the_permalink($p_id_arr[$i]); 1373 $schema_array['name'] = get_the_title($p_id_arr[$i]); 1374 $schema_array['aggregateRating'] = array( 1375 '@type' => 'AggregateRating', 1376 'ratingValue' => $row['dataCount']['avgrtng'], 1377 'reviewCount' => $row['dataCount']['prtng'], 1378 'bestRating' => '5', 1379 'worstRating' => '1', 1380 ); 1381 } 1382 if( $wdtyp == 'star_rating' ) { 1383 $star_rating_html = $row['dataRating'][0]['html']; 1384 1385 update_post_meta( $p_id_arr[$i], 'wiserrw_star_rating_html', $star_rating_html ); 1744 function wiserrw_update_css( $wsid ) { 1745 $url = 'https://rs.wiserreview.com/api/wp/updSchema'; 1746 $body = array( 1747 'wsid' => $wsid, 1748 'updtyp' => 'update_css', 1749 'ver' => WISERRW_PLUGIN_VERSION 1750 ); 1751 $response = wp_remote_post( 1752 $url, 1753 array( 1754 'method' => 'POST', 1755 'headers' => array( 1756 'Content-Type' => 'application/json', 1757 ), 1758 'body' => wp_json_encode( $body ), 1759 'timeout' => 30, 1760 ) 1761 ); 1762 } 1763 1764 function wiserrw_update_schema( $wsid, $pid ) { 1765 $url = 'https://rs.wiserreview.com/api/wp/updSchema'; 1766 $body = array( 1767 'wsid' => $wsid, 1768 'pid' => $pid, 1769 'updtyp' => 'update_schema', 1770 'ver' => WISERRW_PLUGIN_VERSION 1771 ); 1772 $response = wp_remote_post( 1773 $url, 1774 array( 1775 'method' => 'POST', 1776 'headers' => array( 1777 'Content-Type' => 'application/json', 1778 ), 1779 'body' => wp_json_encode( $body ), 1780 'timeout' => 30, 1781 ) 1782 ); 1783 } 1784 1785 function wiserrw_update_html( $wsid, $widget_id ) { 1786 $url = 'https://rs.wiserreview.com/api/wp/updSchema'; 1787 $body = array( 1788 'wsid' => $wsid, 1789 'widgetId' => $widget_id, 1790 'updtyp' => 'update_html', 1791 'ver' => WISERRW_PLUGIN_VERSION 1792 ); 1793 $response = wp_remote_post( 1794 $url, 1795 array( 1796 'method' => 'POST', 1797 'headers' => array( 1798 'Content-Type' => 'application/json', 1799 ), 1800 'body' => wp_json_encode( $body ), 1801 'timeout' => 30, 1802 ) 1803 ); 1804 } 1805 1806 1807 function wiserrw_get_lang_context( $product_id ) { 1808 $ctx = array( 1809 'original_pid' => 0, 1810 'current_pid' => (int) $product_id, 1811 'lang' => '', 1812 ); 1813 1814 if ( function_exists( 'pll_get_post' ) ) { 1815 $ctx['original_pid'] = (int) pll_get_post( $product_id, pll_default_language() ); 1816 $ctx['lang'] = (string) pll_get_post_language( $product_id ); 1817 return $ctx; 1818 } 1819 1820 if ( has_filter( 'wpml_object_id' ) ) { 1821 $trid = apply_filters( 'wpml_element_trid', null, $product_id, 'post_product' ); 1822 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : array(); 1823 if ( is_array( $translations ) ) { 1824 foreach ( $translations as $lang => $t ) { 1825 $eid = is_object($t) ? (int)$t->element_id : (int)($t['element_id'] ?? 0); 1826 if ( ! empty( $t->original ) || ( is_array($t) && ! empty($t['original']) ) ) { 1827 $ctx['original_pid'] = $eid; 1386 1828 } 1387 foreach( $row['data'] as $review_row ) { 1388 1389 1390 $review = array( 1391 "@type" => "Review", 1392 'author' => array( 1393 '@type' => "Person", 1394 'name' => $review_row['un'] 1395 ), 1396 'reviewBody' => $review_row['orgnlrtxt'], 1397 'reviewRating' => array( 1398 '@type' => 'Rating', 1399 'ratingValue' => $review_row['rtng'], 1400 'bestRating' => '5', 1401 'worstRating' => '1', 1402 ), 1403 'publisher' => array( 1404 "@type" => "Organization", 1405 "name" => "WiserReview" 1406 ) 1407 ); 1408 $schema_array['review'][] = $review; 1829 if ( $eid === (int) $product_id ) { 1830 $ctx['lang'] = $lang; 1409 1831 } 1410 $json_schema = json_encode($schema_array,JSON_UNESCAPED_SLASHES);1411 1412 if( $row['dataCount']['prtng'] > 0 ) {1413 update_post_meta( $p_id_arr[$i], 'wiserrw_schema_json', $json_schema );1414 } else {1415 delete_post_meta( $p_id_arr[$i], 'wiserrw_schema_json' );1416 }1417 1832 } 1418 1833 } 1419 wiserrw_send_product_update_on_save_post( $p_id_arr[$i] ); 1420 } 1834 } 1835 1836 return $ctx; 1837 } 1838 1839 function wiserrw_generate_schema( $p_id ) { 1840 if ( empty( $p_id ) || ! is_numeric( $p_id ) ) { 1841 return; 1842 } 1843 1844 // --- Multilingual handling (WPML + Polylang safe) --- 1845 $original_pid = 0; 1846 $current_lang = ''; 1847 1848 // WPML 1849 if ( has_filter( 'wpml_element_trid' ) ) { 1850 $trid = apply_filters( 'wpml_element_trid', null, $p_id, 'post_product' ); 1851 $translations = $trid ? apply_filters( 'wpml_get_element_translations', null, $trid, 'post_product' ) : array(); 1852 $current_lang = apply_filters( 'wpml_current_language', null ); 1853 if ( $translations && is_array( $translations ) ) { 1854 foreach ( $translations as $lang => $translation ) { 1855 if ( ! empty( $translation->original ) ) { 1856 $original_pid = (int) $translation->element_id; 1857 } 1858 } 1859 } 1860 } 1861 1862 // Polylang 1863 if ( function_exists( 'pll_get_post' ) && function_exists( 'pll_current_language' ) ) { 1864 $current_lang = pll_current_language(); 1865 $default_lang = pll_default_language(); 1866 if ( $current_lang && $default_lang && $current_lang !== $default_lang ) { 1867 $original_pid = pll_get_post( $p_id, $default_lang ); 1868 } 1869 } 1870 1871 $opid_value = ( $original_pid && $original_pid !== $p_id ) ? $original_pid : $p_id; 1872 $lang_value = $current_lang ?: ''; 1873 1874 // --- API settings --- 1875 $wiserrw_api_settings = get_option( 'wiserrw_api_settings', array() ); 1876 if ( ! is_array( $wiserrw_api_settings ) || empty( $wiserrw_api_settings['wiserrw_api_key'] ) ) { 1877 return; 1878 } 1879 1880 $wsid = get_option( 'wiserrw_wsid', true ); 1881 $automation_id = get_option( 'wiserrw_automation_id', true ); 1882 if ( ! $wsid || ! $automation_id ) { 1883 return; 1884 } 1885 1886 $url = 'https://rs.wiserreview.com/api/getData'; 1887 $body = array( 1888 'arrType' => array( 'main', 'star_rating' ), 1889 'pid' => (string) $p_id, 1890 'opid' => (string) $opid_value, 1891 'lang' => $lang_value, 1892 'arrPid' => array( (string) $p_id ), 1893 'isFrom' => 'woocommerce', 1894 'wsid' => $wsid, 1895 'updtyp' => 'update_schema', 1896 'ver' => WISERRW_PLUGIN_VERSION 1897 ); 1898 1899 $response = wp_remote_post( 1900 $url, 1901 array( 1902 'method' => 'POST', 1903 'headers' => array( 'Content-Type' => 'application/json' ), 1904 'body' => wp_json_encode( $body ), 1905 'timeout' => 30, 1906 ) 1907 ); 1908 1909 if ( is_wp_error( $response ) ) { 1910 return; 1911 } 1912 1913 $json = wp_remote_retrieve_body( $response ); 1914 $data = json_decode( $json, true ); 1915 if ( empty( $data['data'] ) ) { 1916 return; 1917 } 1918 1919 $schema_array = array(); 1920 $reviewCount = 0; 1921 1922 foreach ( $data['data'] as $row ) { 1923 $wdtyp = $row['wdtyp']; 1924 1925 if ( $wdtyp == 'main' ) { 1926 $reviewCount = intval( $row['dataCount']['prtng'] ); 1927 1928 $schema_array['@context'] = "https://schema.org"; 1929 $schema_array['@type'] = "Product"; 1930 $schema_array['url'] = get_the_permalink( $p_id ); 1931 $schema_array['name'] = get_the_title( $p_id ); 1932 $schema_array['aggregateRating'] = array( 1933 '@type' => 'AggregateRating', 1934 'ratingValue' => $row['dataCount']['avgrtng'], 1935 'reviewCount' => $reviewCount, 1936 'bestRating' => '5', 1937 'worstRating' => '1', 1938 ); 1939 } 1940 1941 if ( $wdtyp == 'star_rating' && !empty( $row['dataRating'][0]['html'] ) ) { 1942 update_post_meta( $p_id, 'wiserrw_star_rating_html', $row['dataRating'][0]['html'] ); 1943 } 1944 1945 if ( ! empty( $row['data'] ) && is_array( $row['data'] ) ) { 1946 foreach ( $row['data'] as $review_row ) { 1947 $schema_array['review'][] = array( 1948 "@type" => "Review", 1949 'author' => array( 1950 '@type' => "Person", 1951 'name' => $review_row['un'] 1952 ), 1953 'reviewBody' => $review_row['orgnlrtxt'], 1954 'reviewRating' => array( 1955 '@type' => 'Rating', 1956 'ratingValue' => $review_row['rtng'], 1957 'bestRating' => '5', 1958 'worstRating' => '1', 1959 ), 1960 'publisher' => array( 1961 "@type" => "Organization", 1962 "name" => "WiserReview" 1963 ) 1964 ); 1965 } 1966 } 1967 } 1968 1969 // ✅ Save or delete only once, after loop 1970 if ( $reviewCount > 0 ) { 1971 update_post_meta( $p_id, 'wiserrw_schema_json', json_encode( $schema_array, JSON_UNESCAPED_SLASHES ) ); 1972 } else { 1973 delete_post_meta( $p_id, 'wiserrw_schema_json' ); 1974 } 1975 1976 wiserrw_send_product_update_on_save_post( $p_id ); 1421 1977 } 1422 1978 … … 1553 2109 'blocking' => true, 1554 2110 'body' => wp_json_encode( $data ), 2111 'ver' => WISERRW_PLUGIN_VERSION 1555 2112 ) 1556 2113 ); … … 1559 2116 1560 2117 } 2118 2119 function wiserrw_show_product_rating_on_collection_woo_events() { 2120 $wiserrw_api_settings = (array) get_option( 'wiserrw_api_settings', [] ); 2121 2122 if ( ! empty( $wiserrw_api_settings['wiserrw_product_card_enabled'] ) && $wiserrw_api_settings['wiserrw_product_card_enabled'] === '1' ) { 2123 echo do_shortcode( '[wiserrw_rating_count]' ); 2124 } 2125 } 2126 2127 add_action( 'we_after_grid_content_html', 'wiserrw_show_product_rating_on_collection_woo_events', 5 ); 2128 2129 // WiserReview Shortcode: [wiserreview_widget id="WidgetID value" type="carousel"] 2130 function wiserreview_widget_shortcode( $atts ) { 2131 $wsid = get_option('wiserrw_wsid',true); 2132 $atts = shortcode_atts( array( 2133 'id' => '', 2134 'type' => '', 2135 ), $atts, 'carousel_widget' ); 2136 2137 if ( empty( $atts['id'] ) ) { 2138 return 'No widget ID provided'; 2139 } 2140 2141 if ( empty( $atts['type'] ) ) { 2142 return 'No widget Type provided'; 2143 } 2144 2145 $widget_id = sanitize_text_field( $atts['id'] ); 2146 $type = sanitize_text_field( $atts['type'] ); 2147 $option_key = 'wiserreview_widget_' . $widget_id; 2148 2149 // 1. If cache exists → serve instantly 2150 $cached_html = get_option( $option_key ); 2151 if ( ! empty( $cached_html ) ) { 2152 return $cached_html; 2153 } 2154 2155 // 2. Render fallback immediately 2156 $fallback = wiserreview_shortcode_fallback( $widget_id, $type ); 2157 2158 // 3. Try API fetch in same request (non-blocking for rendering) 2159 // The user sees fallback, but next time cache will be ready 2160 $api_url = add_query_arg( 2161 array( 2162 'widgetId' => $widget_id, 2163 'type' => $type, 2164 'ver' => WISERRW_PLUGIN_VERSION 2165 ), 2166 'https://rs.wiserreview.com/api/getWdgtHTML' 2167 2168 ); 2169 2170 $response = wp_remote_get( $api_url, array( 'timeout' => 20 ) ); 2171 2172 if ( ! is_wp_error( $response ) ) { 2173 $json = json_decode( wp_remote_retrieve_body( $response ), true ); 2174 if ( isset( $json['html'] ) && ! empty( $json['html'] ) ) { 2175 update_option( $option_key, $json['html'] ); 2176 wiserrw_update_html( $wsid, $widget_id ); 2177 } 2178 } 2179 2180 return $fallback; 2181 } 2182 add_shortcode( 'wiserreview_widget', 'wiserreview_widget_shortcode' ); 2183 2184 function wiserreview_shortcode_fallback( $widget_id, $type ) { 2185 $widget_id = esc_attr( $widget_id ); 2186 2187 return '<script src="https://embed.wiserreview.com/embed/' . $widget_id . '/widget.js" defer></script>' 2188 . '<div data-type="'.$type.'" class="wiser_review_'.$type.'" data-id="' . $widget_id . '"></div>'; 2189 }
Note: See TracChangeset
for help on using the changeset viewer.