PluginProbe
bBlocks – Essential Gutenberg Blocks & Patterns Collection / 2.1.9
bBlocks – Essential Gutenberg Blocks & Patterns Collection v2.1.9
2.1.10 2.1.9 2.1.8 2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.43 2.0.42 2.0.41 2.0.40 2.0.39 2.0.38 trunk 1.0 1.1 1.2 1.3 1.4 1.5 1.5.1 All 110 releases
b-blocks / includes / blocks / woo-product-reviews / WooProductReviews.php

WooProductReviews.php in bBlocks – Essential Gutenberg Blocks & Patterns Collection 2.1.9, at includes/blocks/woo-product-reviews/WooProductReviews.php

167 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Woo Product Reviews — shared server logic.
4 *
5 * Provides attribute sanitization for the read-only Woo Product Reviews block.
6 * The block has NO AJAX endpoint, NO nonce and NO custom REST route — it only
7 * reads approved review data with standard WordPress/WooCommerce functions
8 * (wc_get_product(), get_comments(), get_comment_meta()) at render time.
9 *
10 * @package bBlocks
11 */
12
13 namespace BBlocks\Inc\Blocks;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 class WooProductReviews {
20
21 /**
22 * Allowed sort orders.
23 *
24 * @var string[]
25 */
26 const ORDER_BY = [ 'newest', 'highest_rating', 'lowest_rating' ];
27
28 /**
29 * Sanitize a CSS color value (hex, rgb/hsl, var(), or a CSS keyword).
30 *
31 * @param mixed $color Raw color.
32 * @param string $fallback Fallback when invalid.
33 * @return string
34 */
35 public static function sanitizeColor( $color, $fallback = '' ) {
36 $color = trim( (string) $color );
37 if ( '' === $color ) {
38 return $fallback;
39 }
40 if ( preg_match( '/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/', $color ) ) {
41 return $color;
42 }
43 if ( preg_match( '/^(rgb|rgba|hsl|hsla)\s*\([0-9\s,%.\/]+\)$/i', $color ) ) {
44 return $color;
45 }
46 if ( preg_match( '/^var\(\s*--[a-zA-Z0-9\-_]+\s*(,\s*[a-zA-Z0-9 #%.,\-_\/]+)?\s*\)$/', $color ) ) {
47 return $color;
48 }
49 if ( preg_match( '/^[a-zA-Z]{1,30}$/', $color ) ) {
50 return $color;
51 }
52 return $fallback;
53 }
54
55 /**
56 * Clamp a value to an integer range.
57 *
58 * @param mixed $value Raw value.
59 * @param int $min Minimum.
60 * @param int $max Maximum.
61 * @param int $fallback Fallback when non-numeric.
62 * @return int
63 */
64 public static function clampInt( $value, $min, $max, $fallback ) {
65 if ( ! is_numeric( $value ) ) {
66 return (int) $fallback;
67 }
68 $value = (int) $value;
69 if ( $value < $min ) {
70 return (int) $min;
71 }
72 if ( $value > $max ) {
73 return (int) $max;
74 }
75 return $value;
76 }
77
78 /**
79 * Pick a value from an allowlist.
80 *
81 * @param mixed $value Raw value.
82 * @param string[] $allowed Allowed values.
83 * @param string $fallback Fallback.
84 * @return string
85 */
86 public static function pickFrom( $value, array $allowed, $fallback ) {
87 $value = is_string( $value ) ? trim( $value ) : '';
88 return in_array( $value, $allowed, true ) ? $value : $fallback;
89 }
90
91 /**
92 * Extract a clamped pixel integer from a CSS size string.
93 *
94 * @param mixed $value Raw value (e.g. "15px").
95 * @param int $min Minimum.
96 * @param int $max Maximum.
97 * @param int $fallback Fallback.
98 * @return int
99 */
100 public static function sizeInt( $value, $min, $max, $fallback ) {
101 $num = preg_replace( '/[^0-9]/', '', (string) $value );
102 return self::clampInt( '' === $num ? $fallback : $num, $min, $max, $fallback );
103 }
104
105 /**
106 * Normalize and sanitize the full attribute set into a safe, typed array.
107 *
108 * @param array $attributes Raw block attributes.
109 * @return array
110 */
111 public static function resolveAttributes( array $attributes ) {
112 $nameFont = (array) ( $attributes['nameFontSize'] ?? [] );
113 $bodyFont = (array) ( $attributes['bodyFontSize'] ?? [] );
114 $metaFont = (array) ( $attributes['metaFontSize'] ?? [] );
115 $summaryFont = (array) ( $attributes['summaryRatingFontSize'] ?? [] );
116
117 $noReviewsMessage = isset( $attributes['noReviewsMessage'] ) ? wp_strip_all_tags( (string) $attributes['noReviewsMessage'] ) : '';
118 $noReviewsMessage = '' !== trim( $noReviewsMessage ) ? $noReviewsMessage : __( 'No reviews yet.', 'b-blocks' );
119
120 $noProductMessage = isset( $attributes['noProductMessage'] ) ? wp_strip_all_tags( (string) $attributes['noProductMessage'] ) : '';
121 $noProductMessage = '' !== trim( $noProductMessage ) ? $noProductMessage : __( 'Please select a product.', 'b-blocks' );
122
123 return [
124 'productId' => self::clampInt( $attributes['productId'] ?? 0, 0, PHP_INT_MAX, 0 ),
125 'reviewCount' => self::clampInt( $attributes['reviewCount'] ?? 5, 1, 50, 5 ),
126 'orderBy' => self::pickFrom( $attributes['orderBy'] ?? 'newest', self::ORDER_BY, 'newest' ),
127 'showSummary' => ! isset( $attributes['showSummary'] ) || (bool) $attributes['showSummary'],
128 'showAvatar' => ! isset( $attributes['showAvatar'] ) || (bool) $attributes['showAvatar'],
129 'showDate' => ! isset( $attributes['showDate'] ) || (bool) $attributes['showDate'],
130 'showVerifiedBadge' => ! isset( $attributes['showVerifiedBadge'] ) || (bool) $attributes['showVerifiedBadge'],
131 'noReviewsMessage' => $noReviewsMessage,
132 'noProductMessage' => $noProductMessage,
133 'avatarSize' => self::clampInt( $attributes['avatarSize'] ?? 48, 24, 96, 48 ),
134 'ratingColor' => self::sanitizeColor( $attributes['ratingColor'] ?? '', '#f5a623' ),
135 'ratingEmptyColor' => self::sanitizeColor( $attributes['ratingEmptyColor'] ?? '', '#d1d5db' ),
136 'summaryBG' => self::sanitizeColor( $attributes['summaryBG'] ?? '', 'transparent' ),
137 'summaryTextColor' => self::sanitizeColor( $attributes['summaryTextColor'] ?? '', 'inherit' ),
138 'summaryAccentColor' => self::sanitizeColor( $attributes['summaryAccentColor'] ?? '', '#f5a623' ),
139 'reviewBG' => self::sanitizeColor( $attributes['reviewBG'] ?? '', '#ffffff' ),
140 'reviewTextColor' => self::sanitizeColor( $attributes['reviewTextColor'] ?? '', 'inherit' ),
141 'reviewNameColor' => self::sanitizeColor( $attributes['reviewNameColor'] ?? '', 'inherit' ),
142 'reviewMetaColor' => self::sanitizeColor( $attributes['reviewMetaColor'] ?? '', '#6b7280' ),
143 'cardBorderWidth' => self::clampInt( $attributes['cardBorderWidth'] ?? 1, 0, 8, 1 ),
144 'cardBorderColor' => self::sanitizeColor( $attributes['cardBorderColor'] ?? '', '#e2e8f0' ),
145 'cardRadius' => self::clampInt( $attributes['cardRadius'] ?? 8, 0, 48, 8 ),
146 'cardPadding' => self::clampInt( $attributes['cardPadding'] ?? 20, 8, 64, 20 ),
147 'cardGap' => self::clampInt( $attributes['cardGap'] ?? 16, 0, 64, 16 ),
148 'verifiedBadgeBG' => self::sanitizeColor( $attributes['verifiedBadgeBG'] ?? '', '#dcfce7' ),
149 'verifiedBadgeColor' => self::sanitizeColor( $attributes['verifiedBadgeColor'] ?? '', '#16a34a' ),
150 'maxWidth' => self::clampInt( $attributes['maxWidth'] ?? 0, 0, 1600, 0 ),
151 'blockAlign' => self::pickFrom( $attributes['blockAlign'] ?? 'center', [ 'left', 'center', 'right' ], 'center' ),
152 'nameSizeDesktop' => self::sizeInt( $nameFont['desktop'] ?? '15', 10, 40, 15 ),
153 'nameSizeTablet' => self::sizeInt( $nameFont['tablet'] ?? '14', 10, 36, 14 ),
154 'nameSizeMobile' => self::sizeInt( $nameFont['mobile'] ?? '14', 10, 32, 14 ),
155 'bodySizeDesktop' => self::sizeInt( $bodyFont['desktop'] ?? '15', 10, 40, 15 ),
156 'bodySizeTablet' => self::sizeInt( $bodyFont['tablet'] ?? '14', 10, 36, 14 ),
157 'bodySizeMobile' => self::sizeInt( $bodyFont['mobile'] ?? '14', 10, 32, 14 ),
158 'metaSizeDesktop' => self::sizeInt( $metaFont['desktop'] ?? '13', 9, 32, 13 ),
159 'metaSizeTablet' => self::sizeInt( $metaFont['tablet'] ?? '12', 9, 28, 12 ),
160 'metaSizeMobile' => self::sizeInt( $metaFont['mobile'] ?? '12', 9, 24, 12 ),
161 'summarySizeDesktop' => self::sizeInt( $summaryFont['desktop'] ?? '48', 20, 96, 48 ),
162 'summarySizeTablet' => self::sizeInt( $summaryFont['tablet'] ?? '40', 20, 88, 40 ),
163 'summarySizeMobile' => self::sizeInt( $summaryFont['mobile'] ?? '36', 18, 80, 36 ),
164 ];
165 }
166 }
167