extras
3 weeks ago
rating-extras
3 weeks ago
rating.php
3 weeks ago
text-aliexpress.php
3 weeks ago
text-booking.php
3 weeks ago
text.php
3 weeks ago
text-booking.php
118 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * Smash Balloon Reviews Feed Text Template - Booking.com |
| 5 | * Custom template for Booking.com reviews with title, pros/cons, and photos |
| 6 | * |
| 7 | * @package SmashBalloon\Reviews |
| 8 | * @version 1.0 Reviews Feed by Smash Balloon |
| 9 | */ |
| 10 | |
| 11 | if (! defined('ABSPATH')) { |
| 12 | exit; // Exit if accessed directly |
| 13 | } |
| 14 | |
| 15 | // Check if review has title, pros, cons (review photos render in the media element). |
| 16 | $has_title = !empty($post['title']); |
| 17 | $has_pros = !empty($post['metadata']['pros']); |
| 18 | $has_cons = !empty($post['metadata']['cons']); |
| 19 | |
| 20 | // Per-section read-more. The shared sbr-feed.js truncation only handles a |
| 21 | // single `.sb-item-text` per card, which Booking doesn't have (its body is |
| 22 | // the two pros/cons sections). So each section truncates server-side and |
| 23 | // reveals the rest with a pure-CSS toggle. |
| 24 | // |
| 25 | // The limit follows the feed's "Content Length" setting so Booking honors the |
| 26 | // same trim the customizer preview applies — PostText.js runs |
| 27 | // SbUtils.printText(pros/cons, feedSettings), trimming each section to |
| 28 | // feedSettings.contentLength. Previously this was hard-coded to 140, so the |
| 29 | // setting had no effect on Booking frontend cards. contentLength <= 0 means |
| 30 | // "no limit" (mirrors sbr-feed.js `if (text_limit < 1) text_limit = 99999`). |
| 31 | $sbr_feed_settings = $this->feed->get_settings(); |
| 32 | $prosncons_limit = isset($sbr_feed_settings['contentLength']) |
| 33 | ? intval($sbr_feed_settings['contentLength']) |
| 34 | : 280; |
| 35 | if ($prosncons_limit < 1) { |
| 36 | $prosncons_limit = PHP_INT_MAX; |
| 37 | } |
| 38 | // Stable, page-unique base id for the toggle inputs (mirrors the lightbox id scheme). |
| 39 | $prosncons_uid = ! empty($post['post_id']) |
| 40 | ? 'sbr-rm-' . $post['post_id'] |
| 41 | : 'sbr-rm-' . substr(md5(($post['title'] ?? '') . ($post['metadata']['pros'] ?? '') . ($post['metadata']['cons'] ?? '')), 0, 10); |
| 42 | |
| 43 | /** |
| 44 | * Render a pros/cons body with per-section read-more. Short (cut on a word |
| 45 | * boundary) + a pure-CSS expand label when over the limit; plain text otherwise. |
| 46 | * |
| 47 | * @param string $text Section text (pros or cons). |
| 48 | * @param string $uid Unique id for the toggle input/label pair. |
| 49 | */ |
| 50 | $render_prosncons = function (string $text, string $uid) use ($prosncons_limit): void { |
| 51 | $len = function_exists('mb_strlen') ? mb_strlen($text) : strlen($text); |
| 52 | if ($len <= $prosncons_limit) { |
| 53 | echo sbr_neutralize_shortcodes(sbr_kses_review_text(nl2br($text))); |
| 54 | return; |
| 55 | } |
| 56 | $short = function_exists('mb_substr') ? mb_substr($text, 0, $prosncons_limit) : substr($text, 0, $prosncons_limit); |
| 57 | // Trim back to a word boundary so we don't cut mid-word (matches the JS behaviour). |
| 58 | $last_space = function_exists('mb_strrpos') ? mb_strrpos($short, ' ') : strrpos($short, ' '); |
| 59 | if ($last_space !== false && $last_space > 0) { |
| 60 | $short = function_exists('mb_substr') ? mb_substr($short, 0, $last_space) : substr($short, 0, $last_space); |
| 61 | } |
| 62 | $id = esc_attr($uid); |
| 63 | // All dynamic parts escaped via esc_attr() / sbr_kses_review_text(); static markup is safe. |
| 64 | // a11y (SMASH-1383): the checkbox is the keyboard-operable control — never |
| 65 | // `hidden` (display:none is unfocusable); CSS visually hides it while the |
| 66 | // label stays the pointer affordance. Name lives on the input; the label's |
| 67 | // ellipsis glyph is decorative for AT. |
| 68 | echo '<input type="checkbox" id="' . $id . '" class="sbr-readmore-toggle" aria-label="' . esc_attr__('Show the full text', 'reviews-feed') . '" />' |
| 69 | . '<span class="sbr-readmore-short">' . sbr_neutralize_shortcodes(sbr_kses_review_text(nl2br($short))) |
| 70 | . '<label for="' . $id . '" class="sb-expand sb-readmore-label" aria-hidden="true"><span class="sb-more">…</span></label></span>' |
| 71 | . '<span class="sbr-readmore-full">' . sbr_neutralize_shortcodes(sbr_kses_review_text(nl2br($text))) . '</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 72 | }; |
| 73 | |
| 74 | // Smiley face SVG icons |
| 75 | $pros_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16px" height="16px" style="flex-shrink: 0; margin-right: 8px;"><path fill="#008234" d="M22.5 12c0 5.799-4.701 10.5-10.5 10.5S1.5 17.799 1.5 12 6.201 1.5 12 1.5 22.5 6.201 22.5 12m1.5 0c0-6.627-5.373-12-12-12S0 5.373 0 12s5.373 12 12 12 12-5.373 12-12M5.634 13.5a1.5 1.5 0 0 0-1.414 2 8.25 8.25 0 0 0 15.56 0 1.5 1.5 0 0 0-1.414-2zm0 1.5h12.732a6.75 6.75 0 0 1-12.732 0M16.5 8.625a.375.375 0 1 1 0-.75.375.375 0 0 1 0 .75.75.75 0 0 0 0-1.5 1.125 1.125 0 1 0 0 2.25 1.125 1.125 0 0 0 0-2.25.75.75 0 0 0 0 1.5m-9 0a.375.375 0 1 1 0-.75.375.375 0 0 1 0 .75.75.75 0 0 0 0-1.5 1.125 1.125 0 1 0 0 2.25 1.125 1.125 0 0 0 0-2.25.75.75 0 0 0 0 1.5"/></svg>'; |
| 76 | |
| 77 | $cons_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16px" height="16px" style="flex-shrink: 0; margin-right: 8px;"><path fill="#1a1a1a" d="M22.5 12c0 5.799-4.701 10.5-10.5 10.5S1.5 17.799 1.5 12 6.201 1.5 12 1.5 22.5 6.201 22.5 12m1.5 0c0-6.627-5.373-12-12-12S0 5.373 0 12s5.373 12 12 12 12-5.373 12-12m-5.28 5.667a7.502 7.502 0 0 0-13.444 0 .75.75 0 1 0 1.344.666 6.002 6.002 0 0 1 10.756 0 .75.75 0 0 0 1.344-.666M8.25 9.375a.375.375 0 1 1 0-.75.375.375 0 0 1 0 .75.75.75 0 0 0 0-1.5 1.125 1.125 0 1 0 0 2.25 1.125 1.125 0 0 0 0-2.25.75.75 0 0 0 0 1.5m7.5 0a.375.375 0 1 1 0-.75.375.375 0 0 1 0 .75.75.75 0 0 0 0-1.5 1.125 1.125 0 1 0 0 2.25 1.125 1.125 0 0 0 0-2.25.75.75 0 0 0 0 1.5"/></svg>'; |
| 78 | ?> |
| 79 | |
| 80 | <?php if ($has_title) : ?> |
| 81 | <div class="sb-item-title sb-fs sbr-review-horizontal-element"> |
| 82 | <?php echo sbr_neutralize_shortcodes(esc_html($post['title'])); ?> |
| 83 | </div> |
| 84 | <?php endif; ?> |
| 85 | |
| 86 | <?php |
| 87 | if ($has_pros || $has_cons) : |
| 88 | $allowed_svg = array( |
| 89 | 'svg' => array( |
| 90 | 'xmlns' => array(), |
| 91 | 'viewbox' => array(), |
| 92 | 'width' => array(), |
| 93 | 'height' => array(), |
| 94 | 'style' => array(), |
| 95 | ), |
| 96 | 'path' => array( |
| 97 | 'fill' => array(), |
| 98 | 'd' => array(), |
| 99 | ), |
| 100 | ); |
| 101 | ?> |
| 102 | <div class="sb-item-pros-cons sbr-review-horizontal-element"> |
| 103 | <?php if ($has_pros) : ?> |
| 104 | <div class="sb-item-pros" style="display: flex; align-items: flex-start; margin-bottom: 8px; clear: both;"> |
| 105 | <span class="sb-item-pros-icon" style="display: inline-flex; align-items: center; height: 1lh; flex-shrink: 0;"><?php echo wp_kses($pros_icon, $allowed_svg); ?></span> |
| 106 | <span class="sb-pros-text"><?php $render_prosncons((string) $post['metadata']['pros'], $prosncons_uid . '-pros'); ?></span> |
| 107 | </div> |
| 108 | <?php endif; ?> |
| 109 | |
| 110 | <?php if ($has_cons) : ?> |
| 111 | <div class="sb-item-cons" style="display: flex; align-items: flex-start;"> |
| 112 | <span class="sb-item-cons-icon" style="display: inline-flex; align-items: center; height: 1lh; flex-shrink: 0;"><?php echo wp_kses($cons_icon, $allowed_svg); ?></span> |
| 113 | <span class="sb-cons-text"><?php $render_prosncons((string) $post['metadata']['cons'], $prosncons_uid . '-cons'); ?></span> |
| 114 | </div> |
| 115 | <?php endif; ?> |
| 116 | </div> |
| 117 | <?php endif; ?> |
| 118 |