| @@ -43,8 +43,53 @@ | ||
| 43 | 43 | * |
| 44 | 44 | * @param array $otherInfo A variant's other_info payload |
| 45 | 45 | * @return string|null Error message, or null when valid |
| 46 | 46 | */ |
| 47 | + /** | |
| 48 | + * One resolver for every `photo` a FluentCart record appends: the | |
| 49 | + * uploaded customer photo for the user when one is set, otherwise the | |
| 50 | + * WordPress avatar by user id or email. Gravatar is asked with | |
| 51 | + * default=404, so an address with no avatar of its own fails to load | |
| 52 | + * and the UI falls back to its placeholder icon instead of Gravatar's | |
| 53 | + * stock image. esc_url_raw throughout — the value travels through | |
| 54 | + * JSON, where esc_url's entity-encoded ampersands would break the | |
| 55 | + * query string. | |
| 56 | + * | |
| 57 | + * @param int|string|null $userId user id owning a possible uploaded photo | |
| 58 | + * @param string|null $email fallback identity when there is no user | |
| 59 | + * @return string the photo URL, or '' when avatars are off or the | |
| 60 | + * record carries no identity to resolve one from | |
| 61 | + */ | |
| 62 | + public static function getUserAvatarUrl($userId, $email, int $size = 100): string | |
| 63 | + { | |
| 64 | + if (!get_option('show_avatars')) { | |
| 65 | + return ''; | |
| 66 | + } | |
| 67 | + | |
| 68 | + if ($userId) { | |
| 69 | + $customPhotoUrl = esc_url_raw( | |
| 70 | + (string) get_user_meta((int) $userId, 'fc_customer_photo_url', true) | |
| 71 | + ); | |
| 72 | + | |
| 73 | + if ($customPhotoUrl) { | |
| 74 | + return $customPhotoUrl; | |
| 75 | + } | |
| 76 | + } | |
| 77 | + | |
| 78 | + $userIdOrEmail = ((int) $userId) ?: (string) $email; | |
| 79 | + | |
| 80 | + if (!$userIdOrEmail) { | |
| 81 | + return ''; | |
| 82 | + } | |
| 83 | + | |
| 84 | + $url = get_avatar_url($userIdOrEmail, [ | |
| 85 | + 'size' => $size, | |
| 86 | + 'default' => '404', | |
| 87 | + ]); | |
| 88 | + | |
| 89 | + return $url ? esc_url_raw($url) : ''; | |
| 90 | + } | |
| 91 | + | |
| 47 | 92 | public static function installmentTimesError($otherInfo) |
| 48 | 93 | { |
| 49 | 94 | if (Arr::get($otherInfo, 'installment', 'no') !== 'yes') { |
| 50 | 95 | return null; |
| @@ -2111,6 +2156,40 @@ | ||
| 2111 | 2156 | } |
| 2112 | 2157 | return (int) $order->tax_behavior === 2 |
| 2113 | 2158 | ? esc_html__('(Included)', 'fluent-cart') |
| 2114 | 2159 | : esc_html__('(Excluded)', 'fluent-cart'); |
| 2160 | + } | |
| 2161 | + | |
| 2162 | + /** | |
| 2163 | + * Product placeholder illustration (an open box holding a picture) shown | |
| 2164 | + * wherever a product has no image. The web copy is a 1200px WebP (sharp on 2x screens); email | |
| 2165 | + * clients (Outlook) and PDF renderers get a 300px PNG of the same art. | |
| 2166 | + */ | |
| 2167 | + const PRODUCT_PLACEHOLDER_IMAGE = 'images/product-placeholder.webp'; | |
| 2168 | + const PRODUCT_PLACEHOLDER_EMAIL_IMAGE = 'images/product-placeholder.png'; | |
| 2169 | + | |
| 2170 | + public static function getProductPlaceholderUrl(): string | |
| 2171 | + { | |
| 2172 | + return \FluentCart\App\Vite::getAssetUrl(static::PRODUCT_PLACEHOLDER_IMAGE); | |
| 2173 | + } | |
| 2174 | + | |
| 2175 | + public static function getProductPlaceholderEmailUrl(): string | |
| 2176 | + { | |
| 2177 | + return \FluentCart\App\Vite::getAssetUrl(static::PRODUCT_PLACEHOLDER_EMAIL_IMAGE); | |
| 2178 | + } | |
| 2179 | + | |
| 2180 | + /** | |
| 2181 | + * A product image URL, or the placeholder when there is none. Carts store | |
| 2182 | + * the thumbnail URL in cart_data, so carts built before the illustration | |
| 2183 | + * still carry the old generic icon (images/placeholder.svg); map that too. | |
| 2184 | + */ | |
| 2185 | + public static function productImageOrPlaceholder($url): string | |
| 2186 | + { | |
| 2187 | + $url = is_string($url) ? $url : ''; | |
| 2188 | + | |
| 2189 | + if ($url === '' || preg_match('#/images/placeholder\.svg(\?.*)?$#', $url)) { | |
| 2190 | + return static::getProductPlaceholderUrl(); | |
| 2191 | + } | |
| 2192 | + | |
| 2193 | + return $url; | |
| 2115 | 2194 | } |
| 2116 | 2195 | } |