| 1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; ?> |
| 2 |
<?php |
| 3 |
/** |
| 4 |
* To the store: a customer has submitted a review. |
| 5 |
* |
| 6 |
* @var \FluentCart\App\Models\ProductReview $review |
| 7 |
* @var \FluentCart\App\Models\Product|object|null $product |
| 8 |
*/ |
| 9 |
|
| 10 |
// Handed no review — a preview built without one, or a caller's mistake — |
| 11 |
// there is nothing to say, and a fatal inside a template takes the whole |
| 12 |
// settings screen down with it. |
| 13 |
if (!$review instanceof \FluentCart\App\Models\ProductReview) { |
| 14 |
return; |
| 15 |
} |
| 16 |
|
| 17 |
$productTitle = $product && !empty($product->post_title) |
| 18 |
? (string) $product->post_title |
| 19 |
: __('Unknown Product', 'fluent-cart'); |
| 20 |
|
| 21 |
// The item the review names, when it does — resolved by the same helper every |
| 22 |
// other surface uses, so the email can never disagree with the admin or the |
| 23 |
// storefront about what a review is about. On a copy: the helper sets |
| 24 |
// item_label as an attribute, and this renders inside the submit request on |
| 25 |
// the very model the controller still holds — a label left on it would be |
| 26 |
// written as a column the table does not have if anything saved it. |
| 27 |
$reviewWithItemLabel = clone $review; |
| 28 |
\FluentCart\App\Services\ProductReviewService::attachItemLabels([$reviewWithItemLabel]); |
| 29 |
$itemLabel = trim((string) $reviewWithItemLabel->getAttribute('item_label')); |
| 30 |
if ($itemLabel !== '') { |
| 31 |
/* translators: 1: product title, 2: item (variation) label */ |
| 32 |
$productTitle = sprintf(__('%1$s (%2$s)', 'fluent-cart'), $productTitle, $itemLabel); |
| 33 |
} |
| 34 |
|
| 35 |
$reviewerName = trim((string) $review->reviewer_name) ?: __('Anonymous', 'fluent-cart'); |
| 36 |
$reviewerEmail = trim((string) $review->reviewer_email); |
| 37 |
$rating = max(0, min(5, (int) $review->rating)); |
| 38 |
$isVerified = !empty($review->is_verified); |
| 39 |
$photoCount = (int) $review->media_count; |
| 40 |
$isPending = $review->status === \FluentCart\App\Helpers\Status::REVIEW_PENDING; |
| 41 |
|
| 42 |
$adminUrl = admin_url('admin.php?page=fluent-cart#/reviews/' . (int) $review->id . '/view'); |
| 43 |
?> |
| 44 |
|
| 45 |
<div class="space_bottom_30"> |
| 46 |
<p><?php esc_html_e('Hello,', 'fluent-cart'); ?></p> |
| 47 |
<p> |
| 48 |
<?php |
| 49 |
printf( |
| 50 |
/* translators: 1: the reviewer's name, 2: the product title */ |
| 51 |
esc_html__('%1$s has submitted a new review of %2$s.', 'fluent-cart'), |
| 52 |
'<strong>' . esc_html($reviewerName) . '</strong>', |
| 53 |
'<strong>' . esc_html($productTitle) . '</strong>' |
| 54 |
); |
| 55 |
if ($isPending) { |
| 56 |
echo ' ' . esc_html__('It is waiting for your approval before it appears on the product page.', 'fluent-cart'); |
| 57 |
} else { |
| 58 |
echo ' ' . esc_html__('It is already live on the product page.', 'fluent-cart'); |
| 59 |
} |
| 60 |
?> |
| 61 |
</p> |
| 62 |
</div> |
| 63 |
|
| 64 |
<?php // The review — the same card the reviewer's own emails show. ?> |
| 65 |
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin-bottom:28px;background-color:#f8fafc;border:1px solid #e5e7eb;border-radius:8px;border-collapse:separate;"> |
| 66 |
<tbody> |
| 67 |
<tr> |
| 68 |
<td style="padding:24px 24px 20px;"> |
| 69 |
<?php if ($rating > 0) : ?> |
| 70 |
<?php // Filled and hollow glyphs, and the number in words, so the rating reads without colour. ?> |
| 71 |
<p style="margin:0 0 12px;font-size:22px;line-height:1;letter-spacing:2px;"> |
| 72 |
<span style="color:<?php echo esc_attr(\FluentCart\App\Services\Renderer\ProductReviewRenderer::starColors()['filled']); ?>;"><?php echo str_repeat('★', $rating); ?></span><span style="color:<?php echo esc_attr(\FluentCart\App\Services\Renderer\ProductReviewRenderer::starColors()['empty']); ?>;"><?php echo str_repeat('☆', 5 - $rating); ?></span> |
| 73 |
<span style="font-size:13px;letter-spacing:0;color:#6b7280;vertical-align:middle;"> |
| 74 |
<?php |
| 75 |
printf( |
| 76 |
/* translators: 1: the rating given, out of five */ |
| 77 |
esc_html__('%1$d out of 5', 'fluent-cart'), |
| 78 |
$rating |
| 79 |
); |
| 80 |
?> |
| 81 |
</span> |
| 82 |
</p> |
| 83 |
<?php endif; ?> |
| 84 |
|
| 85 |
<?php if (!empty($review->title)) : ?> |
| 86 |
<p style="margin:0 0 10px;font-size:18px;font-weight:700;line-height:1.4;color:#111827;"><?php echo esc_html($review->title); ?></p> |
| 87 |
<?php endif; ?> |
| 88 |
|
| 89 |
<p style="margin:0;font-size:15px;line-height:1.7;color:#374151;"><?php echo nl2br(esc_html($review->content)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped by esc_html() before nl2br() ?></p> |
| 90 |
</td> |
| 91 |
</tr> |
| 92 |
<tr> |
| 93 |
<td style="padding:12px 24px;border-top:1px solid #e5e7eb;background-color:#f1f5f9;border-radius:0 0 8px 8px;font-size:13px;line-height:1.6;color:#6b7280;"> |
| 94 |
<?php |
| 95 |
// What a moderator needs at a glance: who, whether they bought |
| 96 |
// it, whether there are photos to look at, and where it stands. |
| 97 |
$facts = []; |
| 98 |
|
| 99 |
$facts[] = $reviewerEmail !== '' |
| 100 |
? esc_html($reviewerName) . ' · ' . esc_html($reviewerEmail) |
| 101 |
: esc_html($reviewerName); |
| 102 |
|
| 103 |
if ($isVerified) { |
| 104 |
$facts[] = esc_html__('Verified purchase', 'fluent-cart'); |
| 105 |
} |
| 106 |
|
| 107 |
if ($photoCount > 0) { |
| 108 |
$facts[] = esc_html(sprintf( |
| 109 |
/* translators: 1: number of photos */ |
| 110 |
_n('%1$d photo', '%1$d photos', $photoCount, 'fluent-cart'), |
| 111 |
$photoCount |
| 112 |
)); |
| 113 |
} |
| 114 |
|
| 115 |
$facts[] = $isPending |
| 116 |
? '<span style="color:#b45309;font-weight:600;">' . esc_html__('Pending approval', 'fluent-cart') . '</span>' |
| 117 |
: '<span style="color:#047857;font-weight:600;">' . esc_html__('Approved', 'fluent-cart') . '</span>'; |
| 118 |
|
| 119 |
echo implode('<br/>', $facts); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- each fact is escaped above |
| 120 |
?> |
| 121 |
</td> |
| 122 |
</tr> |
| 123 |
</tbody> |
| 124 |
</table> |
| 125 |
|
| 126 |
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin-bottom:28px;"> |
| 127 |
<tbody> |
| 128 |
<tr> |
| 129 |
<td> |
| 130 |
<a href="<?php echo esc_url($adminUrl); ?>" target="_blank" style="display:inline-block;background-color:#0f172a;color:#ffffff;padding:14px 28px;border-radius:8px;font-size:16px;font-weight:600;text-decoration:none;"> |
| 131 |
<?php $isPending ? esc_html_e('Moderate review', 'fluent-cart') : esc_html_e('View review', 'fluent-cart'); ?> → |
| 132 |
</a> |
| 133 |
</td> |
| 134 |
</tr> |
| 135 |
</tbody> |
| 136 |
</table> |
| 137 |
|
| 138 |
<div class="space_bottom_30"> |
| 139 |
<p style="margin:0;font-size:15px;line-height:1.6;color:#9ca3af;"> |
| 140 |
<?php esc_html_e('You can approve, reply to, or remove this review from the reviews screen.', 'fluent-cart'); ?> |
| 141 |
</p> |
| 142 |
</div> |
| 143 |
|