Changeset 3666763
- Timestamp:
- 08/26/2026 10:33:07 AM (2 months ago)
- Location:
- wiser-review/trunk
- Files:
-
- 5 edited
-
assets/css/wiserrw-admin.css (modified) (3 diffs)
-
assets/js/wiserw-js.js (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
-
views/wiserw-plugin-settings.php (modified) (5 diffs)
-
wiser-review.php (modified) (12 diffs)
Legend:
- Unmodified
- Added
- Removed
-
wiser-review/trunk/assets/css/wiserrw-admin.css
r3531870 r3666763 11 11 flex-direction: column; 12 12 padding: 0 20px; 13 box-sizing: border-box; 14 overflow-x: clip; 13 15 } 14 16 .wr-form-contents { display: contents; } … … 265 267 .wr-order-6 { order: 6; } 266 268 .wr-order-7 { order: 7; } 269 /* Google Customer Reviews sits just above the FAQ. .wr-form-contents uses 270 * display: contents, so this section (which lives inside the main form for 271 * AJAX save-all semantics) is still treated as a direct child of .wr-page 272 * for flex ordering. */ 267 273 .wr-order-8 { order: 8; } 274 .wr-order-9 { order: 9; } 275 276 /* ─── Universal "Saved" flash — positioned absolutely below the switch so it 277 * flashes in briefly on toggle change without shifting siblings (previously 278 * it was appended inline inside .wiserrw_toggle and stole horizontal space, 279 * pushing the widget name to the right and snapping back on fade-out). */ 280 .wiserrw_toggle { position: relative; } 281 .wiserrw_saved_msg { 282 position: absolute !important; 283 top: 100% !important; 284 left: 0 !important; 285 margin: 4px 0 0 !important; 286 padding: 1px 8px !important; 287 font-size: 11px !important; 288 font-weight: 500 !important; 289 color: #16a34a !important; 290 background: #edfaef; 291 border-radius: 999px; 292 line-height: 1.5; 293 white-space: nowrap; 294 pointer-events: none; 295 z-index: 2; 296 } 297 298 /* ─── Google Customer Reviews section ─── 299 * Compact widget-row style: Merchant ID + delivery days sit side-by-side 300 * under the toggle/description block, an inline save button reveals the 301 * implementation-method dropdown, and only the currently-selected method's 302 * detail panel is visible. Matches the density of the other More Features 303 * rows (Rich Snippet, Leave a Review Page). 304 */ 305 .wr-gcr-hidden { display: none !important; } 306 307 /* Config wrapper — plain inline (no boxed panel) so it reads as a 308 * continuation of the description above, matching Leave a Review Page. */ 309 .wr-gcr-config { margin-top: 10px; } 310 311 /* Merchant ID + delivery days side-by-side; wraps on narrow widths. */ 312 .wr-gcr-fields-row { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; } 313 .wr-gcr-field { min-width: 0; } 314 .wr-gcr-field-grow { flex: 1 1 200px; } 315 .wr-gcr-field-narrow { flex: 0 0 130px; } 316 .wr-gcr-field input[type="text"], 317 .wr-gcr-field input[type="number"] { 318 width: 100%; 319 padding: 6px 10px; 320 font-size: 13px; 321 border: 1px solid #8c8f94; 322 border-radius: 4px; 323 box-sizing: border-box; 324 } 325 .wr-gcr-hint { font-size: 11px; color: #757575; margin: 4px 0 0; line-height: 1.5; } 326 327 /* Merchant ID inline hint + error. Hint sits under the input; error only 328 * shows when the field has content that fails the 8–15 digit rule. */ 329 .wr-gcr-field-hint { 330 display: block; 331 font-size: 11px; 332 color: #757575; 333 margin-top: 4px; 334 line-height: 1.4; 335 } 336 .wr-gcr-field-error { 337 display: block; 338 font-size: 11px; 339 color: #d63638; 340 margin-top: 4px; 341 line-height: 1.4; 342 } 343 .wr-gcr-field-error[hidden] { display: none; } 344 .wr-gcr-field input.wr-gcr-input-invalid { 345 border-color: #d63638; 346 box-shadow: 0 0 0 1px #d63638; 347 } 348 349 /* "?" help icon next to a field label. Reuses the existing .wiserrw-tooltip 350 * bubble (right-aligned above the anchor, wraps at 220px). Focusable so 351 * keyboard users can trigger it too. */ 352 .wr-gcr-help-icon { 353 position: relative; 354 display: inline-flex; 355 align-items: center; 356 margin-left: 4px; 357 color: #757575; 358 cursor: help; 359 outline: none; 360 vertical-align: baseline; 361 } 362 .wr-gcr-help-icon .dashicons { 363 font-size: 14px; 364 width: 14px; 365 height: 14px; 366 line-height: 1; 367 } 368 .wr-gcr-help-icon:hover, 369 .wr-gcr-help-icon:focus { color: #2271b1; } 370 .wr-gcr-help-icon:hover .wiserrw-tooltip, 371 .wr-gcr-help-icon:focus .wiserrw-tooltip { visibility: visible; opacity: 1; } 372 373 /* Save row: inline button + hint next to it. */ 374 .wr-gcr-save-row { display: flex; align-items: center; gap: 10px; margin-top: 12px; flex-wrap: wrap; } 375 .wr-gcr-savehint { 376 font-size: 12px; 377 color: #8a6d0b; 378 line-height: 1.4; 379 margin: 0; 380 } 381 382 /* Help block — replaces the old dropdown-driven method picker. Just 383 * documents that auto-injection happens on the WC thank-you page and 384 * exposes the shortcode + JS API as fallback snippets for merchants 385 * whose theme doesn't fire the standard hook. */ 386 .wr-gcr-help { margin-top: 14px; padding-top: 12px; border-top: 1px solid #ececec; font-size: 12px; color: #50575e; line-height: 1.55; } 387 /* Status line — plain paragraph, no icon. Only visible when the feature 388 * toggle is ON (help block is wrapped in a hide-on-off class server-side). */ 389 .wr-gcr-help-status { margin: 0 0 8px; font-size: 12px; line-height: 1.55; color: #50575e; } 390 .wr-gcr-help-status code { 391 display: inline-block; 392 padding: 0 5px; 393 background: #f0f1f3; 394 border-radius: 3px; 395 font-size: 11px; 396 color: #1d2327; 397 } 398 .wr-gcr-help-fallback { margin: 0; } 399 400 /* First-time enrollment warning — subtle amber tint so it reads as an 401 * important prerequisite without being alarming. Placed between the 402 * "auto-injects" success line and the collapsibles so merchants see it 403 * before they wonder why nothing's rendering. */ 404 .wr-gcr-help-warn { 405 display: flex; align-items: flex-start; gap: 6px; 406 margin: 0 0 8px; 407 padding: 8px 10px; 408 background: #fff8e1; 409 border: 1px solid #f0d478; 410 border-radius: 4px; 411 font-size: 12px; 412 color: #7a5b02; 413 line-height: 1.55; 414 } 415 .wr-gcr-help-warn-icon { color: #b58900; font-size: 16px; width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; } 416 .wr-gcr-help-warn a { color: #7a5b02; text-decoration: underline; font-weight: 500; } 417 .wr-gcr-help-warn a:hover { color: #4a3800; } 418 419 /* Setup steps inside the "How to enroll" collapsible. */ 420 .wr-gcr-setup-steps { margin: 0 0 4px; padding-left: 20px; font-size: 12px; line-height: 1.6; color: #50575e; } 421 .wr-gcr-setup-steps li { margin-bottom: 4px; } 422 .wr-gcr-setup-steps li:last-child { margin-bottom: 0; } 423 424 /* Collapsible bordered pill card (Design X). Used for both "First-time 425 * setup" and "Custom / headless checkout" blocks so they read as real 426 * accordions rather than plain link-style text. Left dashicon anchors the 427 * card visually; right chevron rotates on open. Native <details> element 428 * keeps this JS-free. */ 429 .wr-gcr-card { 430 margin-top: 8px; 431 background: #fff; 432 border: 1px solid #dcdcde; 433 border-radius: 6px; 434 overflow: hidden; 435 transition: border-color .15s ease; 436 } 437 .wr-gcr-card + .wr-gcr-card { margin-top: 8px; } 438 .wr-gcr-card:hover { border-color: #b0b3b8; } 439 .wr-gcr-card[open] { border-color: #8c8f94; } 440 441 .wr-gcr-card-summary { 442 display: flex; 443 align-items: center; 444 gap: 8px; 445 padding: 10px 12px; 446 cursor: pointer; 447 font-size: 13px; 448 font-weight: 500; 449 color: #1d2327; 450 list-style: none; 451 user-select: none; 452 } 453 .wr-gcr-card-summary::-webkit-details-marker { display: none; } 454 .wr-gcr-card-summary::marker { content: ""; } 455 .wr-gcr-card-summary:hover { background: #f9fbfe; } 456 .wr-gcr-card[open] > .wr-gcr-card-summary { background: #f8fafc; border-bottom: 1px solid #ececec; } 457 458 .wr-gcr-card-icon { 459 color: #2271b1; 460 font-size: 16px !important; 461 width: 16px !important; 462 height: 16px !important; 463 flex-shrink: 0; 464 } 465 .wr-gcr-card-title { flex: 1; min-width: 0; } 466 .wr-gcr-card-chevron { 467 color: #757575; 468 font-size: 18px !important; 469 width: 18px !important; 470 height: 18px !important; 471 flex-shrink: 0; 472 transition: transform .2s ease; 473 } 474 .wr-gcr-card[open] > .wr-gcr-card-summary > .wr-gcr-card-chevron { 475 transform: rotate(180deg); 476 } 477 .wr-gcr-card-body { 478 padding: 12px; 479 font-size: 12px; 480 color: #50575e; 481 line-height: 1.55; 482 } 483 484 .wr-gcr-snippet-block { padding: 10px 12px; background: #f8fafc; border: 1px solid #dcdcde; border-radius: 4px; } 485 .wr-gcr-snippet-block + .wr-gcr-snippet-block { margin-top: 8px; } 486 .wr-gcr-snippet-label { font-size: 12px; font-weight: 600; color: #1d2327; margin-bottom: 6px; } 487 488 /* Code block copy button (JavaScript-API snippet). */ 489 .wr-gcr-code { position: relative; } 490 .wr-gcr-copy-code { 491 position: absolute; 492 top: 6px; 493 right: 6px; 494 z-index: 1; 495 display: inline-flex; 496 align-items: center; 497 gap: 3px; 498 padding: 3px 8px; 499 font-size: 11px; 500 color: #2271b1; 501 background: #fff; 502 border: 1px solid #dcdcde; 503 border-radius: 4px; 504 cursor: pointer; 505 line-height: 1.6; 506 } 507 .wr-gcr-copy-code:hover { background: #f0f6fc; border-color: #2271b1; } 508 .wr-gcr-copy-code.is-copied { color: #00822b; background: #edfaef; border-color: #a7d7ab; } 509 .wr-gcr-copy-code .dashicons { font-size: 14px; width: 14px; height: 14px; } 510 .wr-gcr-code pre { 511 margin: 0; 512 padding: 10px 12px; 513 background: #fff; 514 border: 1px solid #dcdcde; 515 border-radius: 4px; 516 font-size: 11px; 517 line-height: 1.5; 518 overflow: auto; 519 } 520 521 /* Sidebar-mounted "First-time setup" collapsible. Compact pill card so 522 * the 4-step enrollment path fits inside .wr-feature-guide (260px) 523 * without shouting for attention. The manual-embed snippets card stays 524 * in the main column because its JS code block needs the wider space. */ 525 .wr-gcr-side-setup { 526 display: block; 527 margin: 12px 0 0; 528 border: 1px solid #dcdcde; 529 border-radius: 6px; 530 background: #fff; 531 overflow: hidden; 532 } 533 .wr-gcr-side-setup > summary { 534 list-style: none; 535 display: flex; 536 align-items: center; 537 gap: 6px; 538 padding: 8px 10px; 539 cursor: pointer; 540 font-size: 12px; 541 font-weight: 600; 542 color: #1d2327; 543 background: #f6f7f7; 544 } 545 .wr-gcr-side-setup > summary::-webkit-details-marker { display: none; } 546 .wr-gcr-side-setup > summary::marker { content: ""; } 547 .wr-gcr-side-setup > summary:hover { background: #eef2f7; } 548 .wr-gcr-side-setup[open] > summary { border-bottom: 1px solid #ececec; } 549 .wr-gcr-side-setup > summary > .dashicons:first-child { 550 color: #2271b1; 551 font-size: 14px; 552 width: 14px; 553 height: 14px; 554 } 555 .wr-gcr-side-setup > summary > span:nth-child(2) { 556 flex: 1; 557 min-width: 0; 558 line-height: 1.35; 559 } 560 .wr-gcr-side-chevron { 561 color: #757575; 562 font-size: 14px; 563 width: 14px; 564 height: 14px; 565 transition: transform .15s ease; 566 flex-shrink: 0; 567 } 568 .wr-gcr-side-setup[open] > summary > .wr-gcr-side-chevron { transform: rotate(180deg); } 569 .wr-gcr-side-setup-body { 570 padding: 10px 12px; 571 font-size: 11px; 572 color: #50575e; 573 line-height: 1.5; 574 } 575 .wr-gcr-side-setup-body ol { 576 margin: 0 0 8px; 577 padding-left: 18px; 578 } 579 .wr-gcr-side-setup-body li + li { margin-top: 4px; } 580 .wr-gcr-side-setup-body a { font-weight: 500; } 268 581 269 582 /* ─── FAQ ─── … … 367 680 visibility: hidden; opacity: 0; background-color: #333; color: #fff; 368 681 text-align: center; border-radius: 5px; padding: 6px 8px; font-size: 12px; 369 position: absolute; z-index: 100001; bottom: 125%; left: 50%; 370 transform: translateX(-50%); transition: opacity 0.2s; white-space: nowrap; 682 position: absolute; z-index: 100001; bottom: 125%; 683 right: 0; left: auto; transform: none; 684 transition: opacity 0.2s; 685 max-width: 220px; width: max-content; white-space: normal; line-height: 1.35; 371 686 } 372 687 .wiserrw-tooltip::after { 373 content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px; 688 content: ""; position: absolute; top: 100%; 689 right: 15px; left: auto; margin-left: 0; 374 690 border-width: 5px; border-style: solid; 375 691 border-color: #333 transparent transparent transparent; -
wiser-review/trunk/assets/js/wiserw-js.js
r3531870 r3666763 116 116 117 117 // ✅ CRITICAL: Only attach change handler to CHECKBOX inputs within toggles, NOT submit buttons 118 $(".wiserrw_toggle input[type='checkbox']").on('change', function(e) { 118 // (.wiserrw_autosave_checkbox is the sub-option pattern — small inline checkboxes 119 // outside a .wiserrw_toggle wrapper that still need to persist on change.) 120 $(".wiserrw_toggle input[type='checkbox'], .wiserrw_autosave_checkbox").on('change', function(e) { 119 121 // ✅ Don't process if form is being submitted normally 120 122 if (isFormSubmitting) { -
wiser-review/trunk/readme.txt
r3559378 r3666763 4 4 Donate link: https://wiserreview.com/ 5 5 Requires at least: 5.6 6 Tested up to: 6.96 Tested up to: 7.1 7 7 Requires PHP: 7.4 8 Stable tag: 3. 68 Stable tag: 3.7 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 207 207 208 208 == Changelog == 209 210 = 3.7 = 211 * New: Option to hide the star icon next to the "Leave a Review" My Account menu item (Settings → Leave a Review Page) 212 * Improved: "Leave a Review" inline button and grid now only show on orders with status Completed — no more review invitations on Pending, Cancelled, or Refunded orders 213 * Improved: Google Customer Reviews settings redesigned to match other feature rows; Merchant ID and Delivery days sit side-by-side, and the implementation-method dropdown is removed (plugin always hooks the WooCommerce thank-you page) 214 * Improved: Added first-time enrollment note + setup walkthrough in the Google Customer Reviews help block — flags the "must enroll in Merchant Center first" prerequisite up front 215 * Improved: Google Customer Reviews help block hides itself when the toggle is off 216 * Improved: Clearer "auto-install vs manual snippet" note — green ✓ for standard thank-you page (nothing to do), amber ⚠ for custom / headless / page-builder checkouts (paste snippet) 217 * Improved: Google Merchant ID field now validates format (8–15 digits, numbers only), strips invalid characters as you type, and gates the toggle + Save button until well-formed 218 * Fixed: "Hide the star icon" sub-checkbox now persists correctly on save 219 * Fixed: "Saved" flash after AJAX save no longer widens the toggle column 220 * Fixed: Settings page could show a stray horizontal scrollbar caused by the disabled-state tooltip 221 * Compatibility: Tested with WordPress 7.1 209 222 210 223 = 3.6 = -
wiser-review/trunk/views/wiserw-plugin-settings.php
r3559378 r3666763 82 82 $schema_enabled = ''; 83 83 $review_page_enabled = ''; 84 $review_page_icon_hidden = ''; 84 85 $review_form_url = ''; 86 $gcr_enabled = ''; 87 $gcr_merchant_id = ''; 88 $gcr_delivery_days = '7'; 85 89 86 90 $wiserrw_api_settings = get_option( 'wiserrw_api_settings', array() ); … … 95 99 $schema_enabled = isset( $wiserrw_api_settings['wiserrw_schema_enabled'] ) ? 'checked' : ''; 96 100 $review_page_enabled = isset( $wiserrw_api_settings['wiserrw_review_page_enabled'] ) ? 'checked' : ''; 101 // "Hide" semantic — key absent = icon shown (preserves the look existing 102 // stores already have after update). 103 $review_page_icon_hidden = isset( $wiserrw_api_settings['wiserrw_review_menu_icon_hidden'] ) ? 'checked' : ''; 97 104 $review_form_url = isset( $wiserrw_api_settings['wiserrw_review_form_url'] ) ? $wiserrw_api_settings['wiserrw_review_form_url'] : ''; 105 $gcr_enabled = isset( $wiserrw_api_settings['wiserrw_gcr_enabled'] ) ? 'checked' : ''; 106 $gcr_merchant_id = isset( $wiserrw_api_settings['wiserrw_gcr_merchant_id'] ) ? $wiserrw_api_settings['wiserrw_gcr_merchant_id'] : ''; 107 $gcr_delivery_days = isset( $wiserrw_api_settings['wiserrw_gcr_delivery_days'] ) ? $wiserrw_api_settings['wiserrw_gcr_delivery_days'] : '7'; 98 108 } 99 109 … … 416 426 <?php endif; ?> 417 427 </p> 428 429 <!-- 430 Sub-option: lets merchants on minimalist themes hide the 431 star icon prefixing the "Leave a Review" My Account menu 432 item. "Hide" semantic so the key is absent for new and 433 existing stores and the icon stays on by default — only 434 merchants who explicitly tick this lose the icon. 435 --> 436 <label class="wr-suboption" style="display:flex;align-items:center;gap:8px;margin-top:10px;font-size:12px;color:#50575e;"> 437 <input type="checkbox" 438 class="wiserrw_autosave_checkbox" 439 id="wiserrw_review_menu_icon_hidden_checkbox" 440 name="wiserrw_data[wiserrw_review_menu_icon_hidden]" 441 value="1" 442 <?php echo esc_attr( $review_page_icon_hidden ); ?> 443 <?php disabled( $is_disabled ); ?> /> 444 <span><?php esc_html_e( 'Hide the star icon next to the "Leave a Review" menu item', 'wiser-review' ); ?></span> 445 </label> 418 446 </div> 419 447 </div> … … 441 469 </div> 442 470 471 <?php 472 // Methods are unlocked only once the feature is enabled AND Merchant ID + delivery days are SAVED. 473 $gcr_is_on = ( 'checked' === $gcr_enabled ); 474 $gcr_configured = ( $gcr_is_on && '' !== trim( (string) $gcr_merchant_id ) && '' !== trim( (string) $gcr_delivery_days ) ); 475 $gcr_hide = ' wr-gcr-hidden'; 476 ?> 477 <!-- ─── Google Customer Reviews (renders just above the FAQ via wr-order-8; 478 kept inside the main form so the shared AJAX save persists it alongside 479 all other settings without wiping them). Layout matches the other More 480 Features rows exactly — toggle + name/desc on the left, guide on the 481 right. Merchant ID + Delivery days are ALWAYS visible below the desc 482 so merchants can fill in the ID before the toggle is enable-able 483 (same pattern as Leave a Review Page's URL gate). Rendering method is 484 no longer a picker: the plugin always hooks woocommerce_thankyou, and 485 the shortcode + JS API stay available for custom / headless setups 486 as fallback snippets in a collapsible help block. --><?php 487 // Recompute view-level flags on the possibly-updated merchant-id state. 488 $gcr_mid_empty = '' === trim( (string) $gcr_merchant_id ); 489 ?> 490 <div class="wr-section wr-order-8" id="wiserrw_gcr_section" data-configured="<?php echo $gcr_configured ? '1' : '0'; ?>"> 491 <div class="wr-section-head"> 492 <h2><?php esc_html_e( 'Google Customer Reviews', 'wiser-review' ); ?></h2> 493 <?php if ( ! $is_disabled && $gcr_configured ) : ?> 494 <span class="badge badge--done wr-gcr-active-badge"<?php echo $gcr_is_on ? '' : ' style="display:none;"'; ?>><?php esc_html_e( 'Active', 'wiser-review' ); ?></span> 495 <?php endif; ?> 496 </div> 497 <div class="wr-section-body" style="padding:0 16px;"> 498 499 <div class="wr-feature-row"> 500 <div class="wr-feature-action"> 501 <div class="wiserrw_live_mode wiserrw_toggle"> 502 <label class="switch"> 503 <input type="checkbox" 504 id="wiserrw_gcr_enabled_checkbox" 505 <?php echo esc_attr( $gcr_enabled ); ?> 506 name="wiserrw_data[wiserrw_gcr_enabled]" 507 value="1" 508 <?php disabled( $is_disabled ); ?> 509 <?php if ( $is_disabled ) : ?>data-api-locked="1"<?php endif; ?> 510 <?php if ( ! $is_disabled && $gcr_mid_empty ) : ?>disabled<?php endif; ?>> 511 <span class="slider round"></span> 512 </label> 513 <?php if ( $is_disabled ) : ?> 514 <span class="wiserrw-tooltip"><?php esc_html_e( 'Verify your API key first.', 'wiser-review' ); ?></span> 515 <?php else : ?> 516 <span class="wiserrw-tooltip wr-gcr-tooltip-mid"<?php echo $gcr_mid_empty ? '' : ' style="display:none;"'; ?>><?php esc_html_e( 'Enter your Google Merchant ID first.', 'wiser-review' ); ?></span> 517 <?php endif; ?> 518 </div> 519 <div style="min-width:0; flex:1;"> 520 <div class="wr-fname"><?php esc_html_e( 'Google Customer Reviews', 'wiser-review' ); ?></div> 521 <div class="wr-fdesc"><?php esc_html_e( 'Show the Google Customer Reviews opt-in on the order-confirmation page so Google can collect seller ratings after purchase.', 'wiser-review' ); ?></div> 522 523 <!-- Merchant ID + Delivery days: always visible so the merchant can fill in 524 the ID first (needed to unlock the toggle). Persists via AJAX. --> 525 <div class="wr-gcr-config"> 526 <div class="wr-gcr-fields-row"> 527 <div class="wr-gcr-field wr-gcr-field-grow"> 528 <label class="wr-api-label" for="wiserrw_gcr_merchant_id"><?php esc_html_e( 'Google Merchant ID', 'wiser-review' ); ?> <span class="wiserrw_required_star">*</span><span class="wr-gcr-help-icon" tabindex="0" aria-label="<?php esc_attr_e( 'Find it in Google Merchant Center, top-right next to your account name.', 'wiser-review' ); ?>"><span class="dashicons dashicons-editor-help"></span><span class="wiserrw-tooltip"><?php esc_html_e( 'Find it in Google Merchant Center, top-right next to your account name.', 'wiser-review' ); ?></span></span></label> 529 <input type="text" name="wiserrw_data[wiserrw_gcr_merchant_id]" id="wiserrw_gcr_merchant_id" value="<?php echo esc_attr( $gcr_merchant_id ); ?>" placeholder="<?php esc_attr_e( 'e.g. 123456789', 'wiser-review' ); ?>" autocomplete="off" inputmode="numeric" pattern="\d{8,15}" minlength="8" maxlength="15" aria-describedby="wiserrw_gcr_merchant_id_hint" <?php disabled( $is_disabled ); ?> /> 530 <span class="wr-gcr-field-hint" id="wiserrw_gcr_merchant_id_hint"><?php esc_html_e( '8–15 digits, numbers only.', 'wiser-review' ); ?></span> 531 <span class="wr-gcr-field-error" id="wiserrw_gcr_merchant_id_error" hidden><?php esc_html_e( 'Merchant ID must be 8–15 digits (numbers only).', 'wiser-review' ); ?></span> 532 </div> 533 <div class="wr-gcr-field wr-gcr-field-narrow"> 534 <label class="wr-api-label" for="wiserrw_gcr_delivery_days"><?php esc_html_e( 'Delivery days', 'wiser-review' ); ?></label> 535 <input type="number" min="1" step="1" name="wiserrw_data[wiserrw_gcr_delivery_days]" id="wiserrw_gcr_delivery_days" value="<?php echo esc_attr( $gcr_delivery_days ); ?>" <?php disabled( $is_disabled ); ?> /> 536 </div> 537 </div> 538 539 <div class="wr-gcr-save-row"> 540 <button type="button" class="btn btn--primary wiserrw_gcr_save" <?php disabled( $is_disabled ); ?>><?php esc_html_e( 'Save Changes', 'wiser-review' ); ?></button> 541 </div> 542 </div> 543 544 <!-- Main column: blocking Google-enrollment warning + 545 manual-embed collapsible (needs the wider main column so the 546 JS code block can breathe). The First-time setup collapsible 547 lives in the right sidebar instead, since it's reference info 548 a merchant reads once. --> 549 <div class="wr-gcr-help wr-gcr-help-wrap<?php echo $gcr_is_on ? '' : ' wr-gcr-hidden'; ?>"> 550 <div class="wr-gcr-help-warn"> 551 <span class="dashicons dashicons-info wr-gcr-help-warn-icon"></span> 552 <span> 553 <?php 554 printf( 555 /* translators: %s: link to Google's setup docs */ 556 esc_html__( 'Google requirement: enable Google Customer Reviews in your Merchant Center account and sign the agreement first. The opt-in only appears once Google verifies enrollment. %s', 'wiser-review' ), 557 '<a href="https://support.google.com/merchants/answer/14629305" target="_blank" rel="noopener">' . esc_html__( "See Google's setup guide", 'wiser-review' ) . ' →</a>' 558 ); 559 ?> 560 </span> 561 </div> 562 563 <details class="wr-gcr-card wr-gcr-card-snippets"> 564 <summary class="wr-gcr-card-summary"> 565 <span class="dashicons dashicons-editor-code wr-gcr-card-icon"></span> 566 <span class="wr-gcr-card-title"><?php esc_html_e( 'Add it manually using shortcode or JS code', 'wiser-review' ); ?></span> 567 <span class="dashicons dashicons-arrow-down-alt2 wr-gcr-card-chevron"></span> 568 </summary> 569 <div class="wr-gcr-card-body"> 570 <div style="background:#f6f7f7;border-left:3px solid #c3c4c7;padding:10px 12px;border-radius:0 4px 4px 0;margin:0 0 12px;font-size:13px;line-height:1.5;"> 571 <div style="margin-bottom:6px;"> 572 <span style="color:#0a7d0a;font-weight:700;">✓</span> 573 <strong><?php esc_html_e( 'Standard WooCommerce Thank You page:', 'wiser-review' ); ?></strong> 574 <?php esc_html_e( 'nothing to paste — the opt-in appears automatically as soon as the toggle above is on.', 'wiser-review' ); ?> 575 </div> 576 <div> 577 <span style="color:#b57e00;font-weight:700;">⚠</span> 578 <strong><?php esc_html_e( 'Custom, external, or headless thank-you page:', 'wiser-review' ); ?></strong> 579 <?php esc_html_e( 'auto-install does NOT work — the plugin only hooks the stock Thank You page. If a theme, page-builder (Divi / Elementor), block-based checkout, or a JAMstack front-end replaces the default page, paste one of the snippets below wherever your order confirmation renders.', 'wiser-review' ); ?> 580 </div> 581 </div> 582 <div class="wr-gcr-snippet-block"> 583 <div class="wr-gcr-snippet-label"><?php esc_html_e( 'Shortcode (paste on your custom thank-you page):', 'wiser-review' ); ?></div> 584 <div class="wiserrw_copy_shortcode_wrapper"> 585 <input readonly type="text" class="wiserrw_shortcode_input" value="[wiserrw_google_reviews]" id="wiserrw_google_reviews" /> 586 <span class="dashicons dashicons-admin-page wiserrw_copy_shortcode action" data-shortcode="wiserrw_google_reviews"></span> 587 <span class="wiserrw_copied"><?php esc_html_e( 'Copied!', 'wiser-review' ); ?></span> 588 </div> 589 </div> 590 <div class="wr-gcr-snippet-block"> 591 <div class="wr-gcr-snippet-label"><?php esc_html_e( "Google Customer Reviews JavaScript code (paste on your custom, external, or headless thank-you page and fill in your Merchant ID + order details):", 'wiser-review' ); ?></div> 592 <div class="wr-gcr-code"> 593 <button type="button" class="wr-gcr-copy-code" aria-label="<?php esc_attr_e( 'Copy code', 'wiser-review' ); ?>"> 594 <span class="dashicons dashicons-admin-page"></span> 595 <span class="wr-gcr-copy-txt"><?php esc_html_e( 'Copy', 'wiser-review' ); ?></span> 596 </button> 597 <pre><code class="wr-gcr-js-snippet"><script src="https://apis.google.com/js/platform.js?onload=renderGcrOptIn"></script> 598 <script> 599 window.renderGcrOptIn = function () { 600 gapi.load('surveyoptin', function () { 601 gapi.surveyoptin.render({ 602 "merchant_id": YOUR_MERCHANT_ID, 603 "order_id": "ORDER_ID", 604 "email": "CUSTOMER_EMAIL", 605 "delivery_country": "US", 606 "estimated_delivery_date": "YYYY-MM-DD" 607 }); 608 }); 609 }; 610 </script></code></pre> 611 </div> 612 </div> 613 </div> 614 </details> 615 </div> 616 </div> 617 </div> 618 619 <div class="wr-feature-guide"> 620 <strong><?php esc_html_e( 'How it renders', 'wiser-review' ); ?></strong><br> 621 <?php 622 printf( 623 /* translators: %s: <code>woocommerce_thankyou</code> */ 624 wp_kses( 625 __( 'Auto-injects on the WooCommerce thank-you page after checkout. Works with any theme or page builder that fires the standard %s hook.', 'wiser-review' ), 626 array( 'code' => array() ) 627 ), 628 '<code>woocommerce_thankyou</code>' 629 ); 630 ?> 631 632 <!-- First-time setup: compact <details> so a merchant can scan 633 the 4-step enrollment path inline in the sidebar. Copy stays 634 tight because Google's own guide (linked at the bottom of the 635 card) is the source of truth if Merchant Center's UI shifts. --> 636 <details class="wr-gcr-side-setup"> 637 <summary> 638 <span class="dashicons dashicons-info-outline"></span> 639 <span><?php esc_html_e( 'First-time setup (5 min)', 'wiser-review' ); ?></span> 640 <span class="dashicons dashicons-arrow-down-alt2 wr-gcr-side-chevron"></span> 641 </summary> 642 <div class="wr-gcr-side-setup-body"> 643 <ol> 644 <li><?php esc_html_e( 'In Google Merchant Center, open Settings (gear icon) › Add-ons.', 'wiser-review' ); ?></li> 645 <li><?php esc_html_e( 'Under the Discover tab, find "Google Customer Reviews" and click Add.', 'wiser-review' ); ?></li> 646 <li><?php esc_html_e( 'Open the add-on under "Your Add-ons" and click Sign agreement.', 'wiser-review' ); ?></li> 647 <li><?php esc_html_e( 'Come back here. The opt-in starts appearing on your next order after Google verifies enrollment.', 'wiser-review' ); ?></li> 648 </ol> 649 <a href="https://support.google.com/merchants/answer/14629305" target="_blank" rel="noopener"><?php esc_html_e( "Google's setup guide", 'wiser-review' ); ?> →</a> 650 </div> 651 </details> 652 653 <br> 654 <a href="https://support.google.com/merchants/answer/7106244" target="_blank" rel="noopener"><?php esc_html_e( 'About Google Customer Reviews', 'wiser-review' ); ?> →</a> 655 </div> 656 </div> 657 </div> 658 </div> 659 660 <script> 661 ( function () { 662 var section = document.getElementById( 'wiserrw_gcr_section' ); 663 if ( ! section ) { return; } 664 var toggle = document.getElementById( 'wiserrw_gcr_enabled_checkbox' ); 665 var merchantEl = document.getElementById( 'wiserrw_gcr_merchant_id' ); 666 var errorEl = document.getElementById( 'wiserrw_gcr_merchant_id_error' ); 667 var saveBtnEl = section.querySelector( '.wiserrw_gcr_save' ); 668 var activeBadge = section.querySelector( '.wr-gcr-active-badge' ); 669 var midTooltip = section.querySelector( '.wr-gcr-tooltip-mid' ); 670 var helpBlock = section.querySelector( '.wr-gcr-help-wrap' ); 671 672 // Merchant ID format: numeric, 8–15 digits. 673 var MID_RE = /^\d{8,15}$/; 674 675 function midValue() { return merchantEl ? merchantEl.value.trim() : ''; } 676 function midIsValid() { return MID_RE.test( midValue() ); } 677 678 // Strip non-digits as the user types so the field can only ever hold 679 // digits. Prevents pasted formatted IDs (spaces, dashes) from silently 680 // failing validation and confusing merchants. 681 function sanitiseMid() { 682 if ( ! merchantEl ) { return; } 683 var cleaned = merchantEl.value.replace( /\D+/g, '' ).slice( 0, 15 ); 684 if ( cleaned !== merchantEl.value ) { merchantEl.value = cleaned; } 685 } 686 687 function syncMidError() { 688 if ( ! merchantEl || ! errorEl ) { return; } 689 var v = midValue(); 690 // Show error only when the field has content but is invalid — an 691 // empty field is a "please fill in", not an error state. 692 var showErr = v.length > 0 && ! MID_RE.test( v ); 693 errorEl.hidden = ! showErr; 694 merchantEl.classList.toggle( 'wr-gcr-input-invalid', showErr ); 695 } 696 697 // Live-gate the toggle on the Merchant ID input. Toggle becomes 698 // clickable only once the field holds a well-formed 8–15 digit ID 699 // (no save-then-reload). If the field is cleared or made invalid, 700 // re-lock the toggle and auto-uncheck it so a stale ON state doesn't 701 // persist without a valid Merchant ID. 702 // Skip this gate entirely when the API-key gate is already applied — 703 // data-api-locked wins. 704 function syncToggleGate() { 705 if ( ! toggle ) { return; } 706 if ( toggle.getAttribute( 'data-api-locked' ) === '1' ) { return; } 707 var canEnable = midIsValid(); 708 toggle.disabled = ! canEnable; 709 if ( saveBtnEl ) { saveBtnEl.disabled = ! canEnable; } 710 if ( midTooltip ) { midTooltip.style.display = canEnable ? 'none' : ''; } 711 if ( ! canEnable && toggle.checked ) { 712 toggle.checked = false; 713 syncBadge(); 714 } 715 } 716 function syncBadge() { 717 if ( activeBadge ) { activeBadge.style.display = toggle && toggle.checked ? '' : 'none'; } 718 } 719 // Help block (status + enrollment note + collapsibles) is only useful 720 // once the feature is on. Hide it entirely when the toggle is off so 721 // merchants don't see instructions for something that isn't running. 722 function syncHelpVisibility() { 723 if ( helpBlock ) { helpBlock.classList.toggle( 'wr-gcr-hidden', ! ( toggle && toggle.checked ) ); } 724 } 725 if ( merchantEl ) { 726 merchantEl.addEventListener( 'input', function () { 727 sanitiseMid(); 728 syncMidError(); 729 syncToggleGate(); 730 } ); 731 // Run once on page load in case a stored value is malformed 732 // (legacy installs, manual edits) — surfaces the error early. 733 sanitiseMid(); 734 syncMidError(); 735 } 736 if ( toggle ) { 737 toggle.addEventListener( 'change', function () { 738 syncBadge(); 739 syncHelpVisibility(); 740 } ); 741 } 742 743 // Shared AJAX save (wiserrw_save_settings) — persists ALL settings WITHOUT re-verifying the API key. 744 function ajaxSave() { 745 var form = document.getElementById( 'wiserrw_plugin_settings_form' ); 746 if ( ! form || typeof wiserrw_ajax_var === 'undefined' ) { return Promise.reject(); } 747 var params = new URLSearchParams( new FormData( form ) ); 748 params.append( 'action', 'wiserrw_save_settings' ); 749 params.append( 'nonce', wiserrw_ajax_var.nonce ); 750 return fetch( wiserrw_ajax_var.url, { 751 method: 'POST', 752 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 753 body: params.toString(), 754 credentials: 'same-origin' 755 } ).then( function ( r ) { return r.json(); } ); 756 } 757 758 // "Save Changes" — persists Merchant ID + Delivery days, then reloads 759 // so the server recomputes the Active badge / configured state. 760 var saveBtn = section.querySelector( '.wiserrw_gcr_save' ); 761 if ( saveBtn ) { 762 saveBtn.addEventListener( 'click', function () { 763 if ( typeof wiserrw_ajax_var === 'undefined' ) { return; } 764 var original = saveBtn.textContent; 765 saveBtn.disabled = true; 766 saveBtn.textContent = '<?php echo esc_js( __( 'Saving…', 'wiser-review' ) ); ?>'; 767 ajaxSave() 768 .then( function () { window.location.reload(); } ) 769 .catch( function () { 770 saveBtn.disabled = false; 771 saveBtn.textContent = original; 772 } ); 773 } ); 774 } 775 776 // Copy the Google script-block snippet. 777 var copyCodeBtn = section.querySelector( '.wr-gcr-copy-code' ); 778 if ( copyCodeBtn ) { 779 copyCodeBtn.addEventListener( 'click', function () { 780 var codeEl = section.querySelector( '.wr-gcr-js-snippet' ); 781 if ( ! codeEl ) { return; } 782 var text = codeEl.innerText; 783 var label = copyCodeBtn.querySelector( '.wr-gcr-copy-txt' ); 784 function flash() { 785 if ( ! label ) { return; } 786 var orig = label.getAttribute( 'data-orig' ) || label.textContent; 787 label.setAttribute( 'data-orig', orig ); 788 label.textContent = '<?php echo esc_js( __( 'Copied!', 'wiser-review' ) ); ?>'; 789 copyCodeBtn.classList.add( 'is-copied' ); 790 setTimeout( function () { 791 label.textContent = orig; 792 copyCodeBtn.classList.remove( 'is-copied' ); 793 }, 1500 ); 794 } 795 if ( navigator.clipboard && navigator.clipboard.writeText ) { 796 navigator.clipboard.writeText( text ).then( flash ).catch( function () {} ); 797 } else { 798 var ta = document.createElement( 'textarea' ); 799 ta.value = text; 800 document.body.appendChild( ta ); 801 ta.select(); 802 try { document.execCommand( 'copy' ); flash(); } catch ( e ) {} 803 document.body.removeChild( ta ); 804 } 805 } ); 806 } 807 808 syncToggleGate(); 809 syncBadge(); 810 syncHelpVisibility(); 811 } )(); 812 </script> 813 443 814 <!-- ─── 5. Clear Cache ─── --> 444 815 <div class="wr-actions-bar wr-order-5"> 445 <a class="btn btn--secondary btn--small" href="<?php echo esc_url( admin_url( 'admin.php?page=wiser-review&wiser _clear_cache' ) ); ?>"><?php esc_html_e( 'Clear Cache', 'wiser-review' ); ?></a>816 <a class="btn btn--secondary btn--small" href="<?php echo esc_url( admin_url( 'admin.php?page=wiser-review&wiserrw_clear_cache' ) ); ?>"><?php esc_html_e( 'Clear Cache', 'wiser-review' ); ?></a> 446 817 <span class="wr-actions-bar-label"><?php wiserrw_check_rest_api(); ?></span> 447 818 </div> … … 564 935 </div> 565 936 566 <!-- ─── 8. FAQ─── -->567 <div class="wr-section wr-order- 8">937 <!-- ─── 9. FAQ (renumbered from 8; Google Customer Reviews now sits at wr-order-8 just above the FAQ) ─── --> 938 <div class="wr-section wr-order-9"> 568 939 <div class="wr-section-head"> 569 940 <h2><?php esc_html_e( 'Frequently Asked Questions', 'wiser-review' ); ?></h2> -
wiser-review/trunk/wiser-review.php
r3559378 r3666763 4 4 * Plugin URI: https://wiserreview.com 5 5 * Description: Collect, manage, and display powerful product reviews and testimonials in WooCommerce using the WiserReview platform. Boost trust and conversion with automated review collection. 6 * Version: 3. 66 * Version: 3.7 7 7 * Author: Wiser Notify 8 8 * Author URI: https://wiserreview.com … … 24 24 define( 'WISERRW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 25 25 define( 'WISERRW_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 26 define( 'WISERRW_PLUGIN_VERSION', '3. 6' );26 define( 'WISERRW_PLUGIN_VERSION', '3.7' ); 27 27 define( 'WISERRW_API_HOST', 'https://api.wiserreview.com/api/woocommerce/' ); 28 28 define( 'WISERRW_RS_HOST', 'https://rs.wiserreview.com' ); … … 79 79 $wiserrw_data['wiserrw_review_form_url'] = rtrim( strtok( trim( $wiserrw_data['wiserrw_review_form_url'] ), '?' ), '/' ); 80 80 } 81 82 // Sanitize Google Merchant ID: digits only, 8–15 chars. Anything else 83 // blanks the field AND force-disables the GCR toggle so a malformed ID 84 // can't leave the feature "on" with a broken value. 85 if ( isset( $wiserrw_data['wiserrw_gcr_merchant_id'] ) ) { 86 $gcr_mid_clean = preg_replace( '/\D+/', '', (string) $wiserrw_data['wiserrw_gcr_merchant_id'] ); 87 if ( '' !== $gcr_mid_clean && preg_match( '/^\d{8,15}$/', $gcr_mid_clean ) ) { 88 $wiserrw_data['wiserrw_gcr_merchant_id'] = $gcr_mid_clean; 89 } else { 90 $wiserrw_data['wiserrw_gcr_merchant_id'] = ''; 91 unset( $wiserrw_data['wiserrw_gcr_enabled'] ); 92 } 93 } 81 94 // Force review page toggle off if URL is invalid 82 95 $review_url = $wiserrw_data['wiserrw_review_form_url'] ?? ''; … … 270 283 } 271 284 add_action( 'wp_footer', 'wiserr_review_script' ); 285 286 /* ───────────────────────────────────────────────────────────────────────── 287 * Google Customer Reviews (Google Review Collector) 288 * 289 * Self-contained: the plugin renders Google's opt-in survey directly on the 290 * order-confirmation page using the Merchant ID + delivery-days configured on 291 * the settings page. No WiserReview backend / reviewPixel.js involvement. 292 * Three implementation methods: automatic (woocommerce_thankyou), shortcode, 293 * and a JavaScript API for custom/headless checkouts. 294 * ──────────────────────────────────────────────────────────────────────── */ 295 296 /** 297 * Settings array for the Google Customer Reviews feature. 298 */ 299 function wiserrw_gcr_settings() { 300 return (array) get_option( 'wiserrw_api_settings', array() ); 301 } 302 303 /** 304 * Whether the feature is active. Gated on Live Mode + verified API key 305 * (same requirement as the other widgets), the enable toggle, and a Merchant ID. 306 */ 307 function wiserrw_gcr_is_active() { 308 $s = wiserrw_gcr_settings(); 309 if ( empty( $s['wiserrw_live_mode_checkbox'] ) ) { 310 return false; 311 } 312 if ( (int) get_option( 'wiserrw_api_verified', 0 ) !== 1 ) { 313 return false; 314 } 315 if ( empty( $s['wiserrw_gcr_enabled'] ) ) { 316 return false; 317 } 318 if ( empty( $s['wiserrw_gcr_merchant_id'] ) ) { 319 return false; 320 } 321 return true; 322 } 323 324 /** 325 * Estimated delivery days (defaults to 7). 326 */ 327 function wiserrw_gcr_delivery_days() { 328 $s = wiserrw_gcr_settings(); 329 $d = isset( $s['wiserrw_gcr_delivery_days'] ) ? (int) $s['wiserrw_gcr_delivery_days'] : 7; 330 return $d > 0 ? $d : 7; 331 } 332 333 /** 334 * estimated_delivery_date for Google (YYYY-MM-DD) = today + configured delivery days. 335 */ 336 function wiserrw_gcr_estimated_delivery_date() { 337 return gmdate( 'Y-m-d', time() + ( wiserrw_gcr_delivery_days() * DAY_IN_SECONDS ) ); 338 } 339 340 /** 341 * Build the Google survey payload from a WooCommerce order. 342 */ 343 function wiserrw_gcr_build_payload( $order ) { 344 if ( ! $order instanceof WC_Order ) { 345 return null; 346 } 347 $s = wiserrw_gcr_settings(); 348 349 $payload = array( 350 'merchant_id' => (string) ( $s['wiserrw_gcr_merchant_id'] ?? '' ), 351 'order_id' => (string) $order->get_order_number(), 352 'email' => $order->get_billing_email(), 353 // Google wants the delivery country; prefer shipping, fall back to billing. 354 'delivery_country' => $order->get_shipping_country() ?: $order->get_billing_country(), 355 'estimated_delivery_date' => wiserrw_gcr_estimated_delivery_date(), 356 ); 357 return $payload; 358 } 359 360 /** 361 * Print Google's opt-in survey for a given order. Shared by the automatic and shortcode methods. 362 */ 363 function wiserrw_gcr_render_optin( $order ) { 364 $payload = wiserrw_gcr_build_payload( $order ); 365 if ( empty( $payload ) || empty( $payload['merchant_id'] ) ) { 366 return; 367 } 368 ?> 369 <script> 370 window.wiserRenderGcrOptIn = function () { 371 try { 372 window.gapi.load('surveyoptin', function () { 373 window.gapi.surveyoptin.render(<?php echo wp_json_encode( $payload ); ?>); 374 }); 375 } catch (e) { console.log(e); } 376 }; 377 </script> 378 <script src="https://apis.google.com/js/platform.js?onload=wiserRenderGcrOptIn" async defer></script> 379 <?php 380 } 381 382 /** 383 * Method 1 — Automatic: render on the standard WooCommerce thank-you page. 384 * Always attempts when GCR is active; no picker/method setting to gate on 385 * (matches how the plugin's other widgets auto-attach to WC hooks). If the 386 * theme doesn't fire woocommerce_thankyou (headless / heavily-customized 387 * checkout), this is a silent no-op and the merchant falls back to the 388 * shortcode or JS API — both are always available for that reason. 389 */ 390 function wiserrw_gcr_thankyou( $order_id ) { 391 if ( ! wiserrw_gcr_is_active() ) { 392 return; 393 } 394 $order = wc_get_order( $order_id ); 395 if ( $order ) { 396 wiserrw_gcr_render_optin( $order ); 397 } 398 } 399 add_action( 'woocommerce_thankyou', 'wiserrw_gcr_thankyou', 20 ); 400 401 /** 402 * Method 2 — Shortcode: [wiserrw_google_reviews] for custom / replaced thank-you pages. 403 * Resolves the order from the order-received page and verifies the order key. 404 * Always registered when GCR is active; merchants who need it on a custom 405 * page just paste it. 406 */ 407 function wiserrw_gcr_shortcode() { 408 if ( ! wiserrw_gcr_is_active() ) { 409 return ''; 410 } 411 412 $order_id = absint( get_query_var( 'order-received' ) ); 413 if ( ! $order_id && isset( $_GET['order-received'] ) ) { 414 $order_id = absint( wp_unslash( $_GET['order-received'] ) ); 415 } 416 if ( ! $order_id ) { 417 return ''; 418 } 419 420 $order = wc_get_order( $order_id ); 421 if ( ! $order ) { 422 return ''; 423 } 424 // Verify the order key so the shortcode can't be used to render an arbitrary order. 425 $key = isset( $_GET['key'] ) ? sanitize_text_field( wp_unslash( $_GET['key'] ) ) : ''; 426 if ( $key && ! hash_equals( $order->get_order_key(), $key ) ) { 427 return ''; 428 } 429 430 ob_start(); 431 wiserrw_gcr_render_optin( $order ); 432 return ob_get_clean(); 433 } 434 add_shortcode( 'wiserrw_google_reviews', 'wiserrw_gcr_shortcode' ); 272 435 273 436 … … 512 675 513 676 // Extract wrapper 514 $wiser _open_tag = '';677 $wiserrw_open_tag = ''; 515 678 if ( preg_match( '/<div\b[^>]*>/i', $product_rating_count, $matches ) ) { 516 $wiser _open_tag = $matches[0];679 $wiserrw_open_tag = $matches[0]; 517 680 518 681 // Ensure opid 519 if ( preg_match( '/data-opid=/', $wiser _open_tag ) ) {520 $wiser _open_tag = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $wiser_open_tag );682 if ( preg_match( '/data-opid=/', $wiserrw_open_tag ) ) { 683 $wiserrw_open_tag = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $wiserrw_open_tag ); 521 684 } else { 522 $wiser _open_tag = str_replace( '<div', '<div data-opid="' . esc_attr( $opid_value ) . '"', $wiser_open_tag );685 $wiserrw_open_tag = str_replace( '<div', '<div data-opid="' . esc_attr( $opid_value ) . '"', $wiserrw_open_tag ); 523 686 } 524 687 525 688 // Ensure lang 526 689 if ( ! empty( $current_lang ) ) { 527 if ( preg_match( '/data-lang=/', $wiser _open_tag ) ) {528 $wiser _open_tag = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $wiser_open_tag );690 if ( preg_match( '/data-lang=/', $wiserrw_open_tag ) ) { 691 $wiserrw_open_tag = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $wiserrw_open_tag ); 529 692 } else { 530 $wiser _open_tag = str_replace( '<div', '<div data-lang="' . esc_attr( $current_lang ) . '"', $wiser_open_tag );693 $wiserrw_open_tag = str_replace( '<div', '<div data-lang="' . esc_attr( $current_lang ) . '"', $wiserrw_open_tag ); 531 694 } 532 695 } … … 556 719 // Build output 557 720 $output = ''; 558 if ( $wiser _open_tag ) {559 $output .= $wiser _open_tag;721 if ( $wiserrw_open_tag ) { 722 $output .= $wiserrw_open_tag; 560 723 } 561 724 $output .= $inner_html; … … 668 831 669 832 // --- Extract the wrapper div tag --- 670 $wiser _open_tag = '';833 $wiserrw_open_tag = ''; 671 834 if ( preg_match( '/<div\b[^>]*>/i', $product_review_section, $matches ) ) { 672 $wiser _open_tag = $matches[0];835 $wiserrw_open_tag = $matches[0]; 673 836 674 837 // --- Ensure opid for wrapper --- 675 if ( preg_match( '/data-opid=/', $wiser _open_tag ) ) {676 $wiser _open_tag = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $wiser_open_tag );838 if ( preg_match( '/data-opid=/', $wiserrw_open_tag ) ) { 839 $wiserrw_open_tag = preg_replace( '/data-opid=("|\')(.*?)\1/', 'data-opid="' . $opid_value . '"', $wiserrw_open_tag ); 677 840 } else { 678 $wiser _open_tag = str_replace( '<div', '<div data-opid="' . esc_attr( $opid_value ) . '"', $wiser_open_tag );841 $wiserrw_open_tag = str_replace( '<div', '<div data-opid="' . esc_attr( $opid_value ) . '"', $wiserrw_open_tag ); 679 842 } 680 843 681 844 // --- Ensure lang for wrapper --- 682 845 if ( ! empty( $current_lang ) ) { 683 if ( preg_match( '/data-lang=/', $wiser _open_tag ) ) {684 $wiser _open_tag = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $wiser_open_tag );846 if ( preg_match( '/data-lang=/', $wiserrw_open_tag ) ) { 847 $wiserrw_open_tag = preg_replace( '/data-lang=("|\')(.*?)\1/', 'data-lang="' . $current_lang . '"', $wiserrw_open_tag ); 685 848 } else { 686 $wiser _open_tag = str_replace( '<div', '<div data-lang="' . esc_attr( $current_lang ) . '"', $wiser_open_tag );849 $wiserrw_open_tag = str_replace( '<div', '<div data-lang="' . esc_attr( $current_lang ) . '"', $wiserrw_open_tag ); 687 850 } 688 851 } … … 712 875 // Build output 713 876 $output = ''; 714 if ( $wiser _open_tag ) {715 $output .= $wiser _open_tag;877 if ( $wiserrw_open_tag ) { 878 $output .= $wiserrw_open_tag; 716 879 } 717 880 $output .= $inner_html; … … 910 1073 } 911 1074 1075 // Only fulfilled orders — an un-shipped / cancelled / refunded order 1076 // shouldn't invite a review. Matches the inline order-item button gate. 912 1077 $orders = wc_get_orders( array( 913 1078 'customer_id' => $customer_id, 914 'status' => array( 'completed' , 'processing'),1079 'status' => array( 'completed' ), 915 1080 'limit' => -1, 916 1081 'orderby' => 'date', … … 1106 1271 text-decoration: none; opacity: 0.85; transition: opacity .15s; margin-top: 6px; 1107 1272 } 1108 .wiserrw-inline-review-link:hover { opacity: 1; text-decoration: none; } 1109 .woocommerce-MyAccount-navigation ul li.woocommerce-MyAccount-navigation-link--leave-a-review a::before { 1110 content: ""; 1111 display: inline-block; 1112 width: 16px; height: 16px; 1113 margin-right: 6px; 1114 vertical-align: middle; 1115 background: currentColor; 1116 -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\'%3E%3Cpath d=\'M10 1.12l2.77 5.61 6.19.9-4.48 4.37 1.06 6.17L10 15.27l-5.54 2.9 1.06-6.17L1.04 7.63l6.19-.9L10 1.12z\'/%3E%3C/svg%3E") center/contain no-repeat; 1117 mask: url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\'%3E%3Cpath d=\'M10 1.12l2.77 5.61 6.19.9-4.48 4.37 1.06 6.17L10 15.27l-5.54 2.9 1.06-6.17L1.04 7.63l6.19-.9L10 1.12z\'/%3E%3C/svg%3E") center/contain no-repeat; 1118 }'; 1273 .wiserrw-inline-review-link:hover { opacity: 1; text-decoration: none; }'; 1274 1275 // Star icon prefix on the "Leave a Review" menu item. Default: shown. 1276 // Merchants on minimalist themes can opt out via the sub-checkbox in the 1277 // settings page (key 'wiserrw_review_menu_icon_hidden'). Existing stores 1278 // without the key set keep the icon — preserves the look they shipped with. 1279 if ( empty( $settings['wiserrw_review_menu_icon_hidden'] ) ) { 1280 $css .= ' 1281 .woocommerce-MyAccount-navigation ul li.woocommerce-MyAccount-navigation-link--leave-a-review a::before { 1282 content: ""; 1283 display: inline-block; 1284 width: 16px; height: 16px; 1285 margin-right: 6px; 1286 vertical-align: middle; 1287 background: currentColor; 1288 -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\'%3E%3Cpath d=\'M10 1.12l2.77 5.61 6.19.9-4.48 4.37 1.06 6.17L10 15.27l-5.54 2.9 1.06-6.17L1.04 7.63l6.19-.9L10 1.12z\'/%3E%3C/svg%3E") center/contain no-repeat; 1289 mask: url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 20 20\'%3E%3Cpath d=\'M10 1.12l2.77 5.61 6.19.9-4.48 4.37 1.06 6.17L10 15.27l-5.54 2.9 1.06-6.17L1.04 7.63l6.19-.9L10 1.12z\'/%3E%3C/svg%3E") center/contain no-repeat; 1290 }'; 1291 } 1119 1292 wp_register_style( 'wiserrw-review-link', false ); 1120 1293 wp_enqueue_style( 'wiserrw-review-link' ); … … 1129 1302 } 1130 1303 if ( ! $item instanceof WC_Order_Item_Product ) { 1304 return; 1305 } 1306 // Only show for fulfilled orders — an un-shipped / cancelled / refunded 1307 // order shouldn't invite a review. Matches WooCommerce's convention that 1308 // "Completed" means the customer has physically received the product. 1309 if ( ! $order || 'completed' !== $order->get_status() ) { 1131 1310 return; 1132 1311 } … … 3230 3409 3231 3410 function wiserrw_clear_wp_cache() { 3232 if( isset( $_GET['wiser _clear_cache'] ) && ( current_user_can( 'administrator' ) || current_user_can( 'editor' ) ) ) {3411 if( isset( $_GET['wiserrw_clear_cache'] ) && ( current_user_can( 'administrator' ) || current_user_can( 'editor' ) ) ) { 3233 3412 3234 3413 global $wp_fastest_cache;
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)