review = $review; $this->productName = $productName; } public function render() { $heading = $this->productName /* translators: %s - product name */ ? sprintf(__("%s's Review", 'fluent-cart'), $this->productName) : __('Review Thread', 'fluent-cart'); $approvedReplies = $this->review->replies() ->where('status', Status::REVIEW_APPROVED) ->orderBy('created_at', 'ASC') ->orderBy('id', 'ASC') ->limit(50) ->get(); ?>

renderAuthorRow($this->review); ?> review]); $itemLabel = trim((string) $this->review->getAttribute('item_label')); ?>
review->title) : ?>
review->title); ?>
review->content); ?>
renderMedia($this->review); ?>
isEmpty()) : ?>
renderReply($reply); ?>
ProductReview] */ do_action('fluent_cart/review/modal_footer', ['review' => $this->review]); ?>
renderAuthorRow($reply); ?>
content); ?>
renderReply($reply); return (string) ob_get_clean(); } /** * Author line for a thread entry — the review itself or one of its * replies, both of which are ProductReview rows. */ /** * The avatar for one thread row: the row's photo when there is one, a * person icon as the placeholder underneath. A photo that fails to load * — Gravatar default=404 does exactly that for an address with no * avatar — removes itself and the icon shows, so the markup needs no * script of its own. * * @param ProductReview $entry * @param string $authorName * @return void */ protected function renderAvatar(ProductReview $entry, $authorName) { echo ReviewThreadMarkup::avatarHtml((string) $entry->photo); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } protected function renderAuthorRow(ProductReview $entry) { $authorName = $entry->reviewer_name ?: __('Anonymous', 'fluent-cart'); ?>
renderAvatar($entry, $authorName); ?>
rating > 0) : ?> renderStars((int) $entry->rating); ?>
relativeDate($entry->created_at)); ?>
media; if (empty($mediaItems)) { return; } ?>
%s', esc_html(sprintf( /* translators: %d - star rating out of five */ __('Rated %d out of 5', 'fluent-cart'), $rating )) ); for ($position = 1; $position <= 5; $position++) { $starClass = $position <= $rating ? 'fct-star fct-star-filled' : 'fct-star fct-star-empty'; echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } /** * Matches the storefront's relative-time labels so a server-rendered * modal reads the same as the JS-rendered list behind it. */ protected function relativeDate($createdAt) { return ReviewThreadMarkup::relativeDate($createdAt); } }