PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.0
1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 All 50 releases
← All changes | app/Modules/Templating/AssetLoader.php +191 -4 1.3.28 → 1.7.0 View file →
@@ -78,10 +78,27 @@
78 78
79 79 public static function register()
80 80 {
81 81 add_action('wp_enqueue_scripts', [self::class, 'enqueueAssets']);
82 + add_action('template_redirect', [self::class, 'markOrderReviewPageNoIndex']);
82 83 }
83 84
85 + /**
86 + * The store's chosen order-review page is reached from an email with a
87 + * bearer credential in its URL and must not be indexed. The shortcode
88 + * renders inside the page body, after wp_head() has already printed the
89 + * robots tag, so the page has to be flagged before the head — here, once
90 + * the main query knows which page it is.
91 + *
92 + * @return void
93 + */
94 + public static function markOrderReviewPageNoIndex()
95 + {
96 + if (TemplateService::getCurrentFcPageType() === 'order_review') {
97 + add_filter('wp_robots', 'wp_robots_no_robots');
98 + }
99 + }
100 +
84 101 public static function enqueueAssets()
85 102 {
86 103 $pageType = TemplateService::getCurrentFcPageType();
87 104
@@ -101,13 +118,141 @@
101 118 break;
102 119 case 'checkout':
103 120 self::loadCheckoutAssets();
104 121 break;
122 + case 'order_review':
123 + // The store's chosen review page: enqueued here, in the head,
124 + // rather than mid-body by the shortcode, so the form and the
125 + // page do not paint unstyled first.
126 + self::loadReviewSubmissionFormAssets();
127 + Vite::enqueueStyle('fluent-cart-order-review', 'public/order-review/order-review.scss');
128 + break;
105 129 default:
106 130 return;
107 131 }
108 132 }
109 133
134 + // Shared between the full single-product bundle and the review-form-only
135 + // one, so a form rendered either way is enqueued under one handle and
136 + // localized with one array.
137 + const REVIEW_FORM_SCRIPT_HANDLE = 'fluent-cart-review-form';
138 + const REVIEW_FORM_STYLE_HANDLE = 'fluent-cart-reviews';
139 +
140 + /**
141 + * Just enough to run a review form: the form script, the review
142 + * stylesheet, and the strings the script reads.
143 + *
144 + * A review page is not a product page. loadSingleProductAssets() also
145 + * carries gallery zoom, the single-product behaviour, the review list,
146 + * the product-card assets and the add-to-cart and direct-checkout
147 + * styles — none of which a standalone form uses, and all of which every
148 + * visitor to that page would download and execute.
149 + *
150 + * Idempotent, and idempotent against the full bundle too: both enqueue
151 + * the same file under REVIEW_FORM_SCRIPT_HANDLE, so whichever runs
152 + * second is dropped by WordPress rather than loading the script twice
153 + * and giving every form two controllers.
154 + */
155 + public static function loadReviewSubmissionFormAssets()
156 + {
157 + self::markFrontendAssetsRequired();
158 +
159 + if (wp_script_is(self::REVIEW_FORM_SCRIPT_HANDLE, 'enqueued')) {
160 + return;
161 + }
162 +
163 + // Through the same helper the full bundle uses, so the version and
164 + // the localization are stamped on identically.
165 + Vite::enqueueAllScripts(
166 + [
167 + [
168 + 'source' => 'public/single-product/ReviewForm.js',
169 + 'dependencies' => [],
170 + 'inFooter' => true,
171 + 'handle' => self::REVIEW_FORM_SCRIPT_HANDLE,
172 + ],
173 + ],
174 + self::REVIEW_FORM_SCRIPT_HANDLE,
175 + [
176 + 'fluentcart_review_vars' => static::reviewJsVars(),
177 + ]
178 + );
179 +
180 + Vite::enqueueStyle(
181 + self::REVIEW_FORM_STYLE_HANDLE,
182 + 'public/single-product/reviews.scss'
183 + );
184 +
185 + /**
186 + * The review form's assets are on this page. An add-on that extends
187 + * the form (Pro's photo upload) enqueues its own script and styles
188 + * here, so a surface that loads the form without being a product
189 + * page — the customer dashboard, the order-review page — gets them
190 + * too. Fired once per request, when the assets are first enqueued.
191 + *
192 + * @param array $context empty for now; the single payload every
193 + * action carries, so listeners keep one
194 + * signature as context is added
195 + */
196 + do_action('fluent_cart/review/submission_form_assets_loaded', []);
197 + }
198 +
199 + /**
200 + * @return array
201 + */
202 + public static function reviewJsVars(): array
203 + {
204 + $reviewVars = [
205 + // Threaded replies (customer replies + multiple store replies)
206 + // are a Pro capability, off by default. The frontend uses this to
207 + // decide whether to render the reviewer reply form in the modal.
208 + 'trans' => [
209 + 'no_reviews' => __('No reviews yet. Be the first to write a review!', 'fluent-cart'),
210 + 'verified_purchase' => __('Verified Purchase', 'fluent-cart'),
211 + 'rating_required' => __('Please select a rating', 'fluent-cart'),
212 + 'photo_preview' => __('Photo preview', 'fluent-cart'),
213 + /* translators: %s: number of attachments still behind the + */
214 + 'view_more_attachments' => __('View %s more attachments', 'fluent-cart'),
215 + 'content_required' => __('Please write a review', 'fluent-cart'),
216 + 'name_required' => __('Please enter your name', 'fluent-cart'),
217 + 'email_required' => __('Please enter your email', 'fluent-cart'),
218 + 'submitting' => __('Submitting...', 'fluent-cart'),
219 + 'submit_review' => __('Submit Review', 'fluent-cart'),
220 + 'store_reply' => __('Store Reply', 'fluent-cart'),
221 + 'reviewer' => __('Reviewer', 'fluent-cart'),
222 + 'reply' => __('Reply', 'fluent-cart'),
223 + 'view_reply' => __('View Reply', 'fluent-cart'),
224 + 'review_thread' => __('Review Thread', 'fluent-cart'),
225 + 'no_replies' => __('No replies yet.', 'fluent-cart'),
226 + 'close' => __('Close', 'fluent-cart'),
227 + 'all' => __('All', 'fluent-cart'),
228 + 'prev' => __('Prev', 'fluent-cart'),
229 + 'next' => __('Next', 'fluent-cart'),
230 + 'prev_page' => __('Previous page', 'fluent-cart'),
231 + 'next_page' => __('Next page', 'fluent-cart'),
232 + /* translators: 1: the page this segment goes to, 2: how many there are. */
233 + 'slider_page_of' => __('Page %1$s of %2$s', 'fluent-cart'),
234 + 'read_more' => __('Read more', 'fluent-cart'),
235 + 'show_less' => __('Show less', 'fluent-cart'),
236 + 'load_error' => __('Unable to load reviews. Please try again later.', 'fluent-cart'),
237 + 'edit_review' => __('Edit your review', 'fluent-cart'),
238 + 'rated_out_of' => __('Rated %d out of 5', 'fluent-cart'),
239 + /* translators: 1: current step, 2: total steps */
240 + 'step_x_of_y' => __('Step %1$s of %2$s', 'fluent-cart'),
241 + 'rating_poor' => __('Poor', 'fluent-cart'),
242 + 'rating_average' => __('Average', 'fluent-cart'),
243 + 'rating_good' => __('Good', 'fluent-cart'),
244 + 'rating_very_good' => __('Very Good', 'fluent-cart'),
245 + 'rating_excellent' => __('Excellent', 'fluent-cart'),
246 + 'rating_step_hint' => __('Rate this product', 'fluent-cart'),
247 + 'details_step_hint' => __('Tell us more', 'fluent-cart'),
248 + 'photos_step_hint' => __('Photos are optional', 'fluent-cart'),
249 + ],
250 + ];
251 +
252 + return apply_filters('fluent_cart/review/js_vars', $reviewVars);
253 + }
254 +
110 255 public static function loadSingleProductAssets()
111 256 {
112 257 self::markFrontendAssetsRequired();
113 258 static $isLoaded = false;
@@ -125,11 +270,26 @@
125 270 [
126 271 'source' => 'public/single-product/SingleProduct.js',
127 272 'dependencies' => [],
128 273 'inFooter' => true
274 + ],
275 + [
276 + 'source' => 'public/single-product/Reviews.js',
277 + 'dependencies' => [],
278 + 'inFooter' => true
279 + ],
280 + [
281 + 'source' => 'public/single-product/ReviewForm.js',
282 + 'dependencies' => [],
283 + 'inFooter' => true,
284 + // Named, not numbered, so loadReviewSubmissionFormAssets() enqueueing
285 + // the same file lands on the same handle and WordPress drops
286 + // the second one.
287 + 'handle' => self::REVIEW_FORM_SCRIPT_HANDLE,
129 288 ]
130 289 ];
131 290 $localizeData = [
291 + 'fluentcart_review_vars' => static::reviewJsVars(),
132 292 'fluentcart_single_product_vars' => [
133 293 'trans' => TransStrings::singleProductPageString(),
134 294 'cart_button_text' => apply_filters('fluent_cart/product/add_to_cart_text', __('Add To Cart', 'fluent-cart'), []),
135 295 // App::storeSettings()->get('cart_button_text', __('Add to Cart', 'fluent-cart')),
@@ -146,8 +306,9 @@
146 306
147 307 $singlePageStyles = [
148 308 'public/single-product/single-product.scss',
149 309 'public/single-product/similar-product.scss',
310 + 'public/single-product/reviews.scss',
150 311 'public/product-card/style/product-card.scss',
151 312 'public/single-product/xzoom/xzoom.css',
152 313 'public/buttons/add-to-cart/style/style.scss',
153 314 'public/buttons/direct-checkout/style/style.scss'
@@ -342,15 +503,30 @@
342 503 Vite::enqueueStyle('fluentcart-customer-css',
343 504 'public/customer-profile/style/customer-profile.scss'
344 505 );
345 506
507 + // My Reviews opens the storefront review form in a modal, fetched
508 + // per product; the form's own script and stylesheet have to be on
509 + // the page before that markup arrives.
510 + if (\FluentCart\Api\ModuleSettings::isActive('reviews')) {
511 + self::loadReviewSubmissionFormAssets();
512 + }
513 +
514 + // wp-i18n: this bundle pulls in the admin translator transitively
515 + // (Start.js -> @/Bits/common.js -> @/utils/translator/Translator.js),
516 + // which resolves strings through window.wp.i18n.
346 517 Vite::enqueueScript(
347 518 'fluentcart-customer-js',
348 519 'public/customer-profile/Start.js',
349 - []
520 + ['wp-i18n']
350 521 )->with(CustomerProfileHandler::getLocalizationData());
351 522
352 - //will add script here/ skipping for now
523 + /**
524 + * Enqueue additional customer-dashboard assets — the hook an add-on
525 + * uses to ship the JS backing a portal section it registers through
526 + * `fluent_cart/customer_portal/profile_sections`.
527 + */
528 + do_action('fluent_cart/customer_dashboard/enqueue_assets');
353 529 }
354 530
355 531 public static function loadCartAssets()
356 532 {
@@ -403,9 +579,9 @@
403 579 'rest' => Helper::getRestInfo(),
404 580 'ajaxurl' => admin_url('admin-ajax.php'),
405 581 ],
406 582 'fluentcart_drawer_vars' => [
407 - 'placeholder_image' => Vite::getAssetUrl('images/placeholder.svg'),
583 + 'placeholder_image' => Helper::getProductPlaceholderUrl(),
408 584 'cart_item_layout' => $cartItemLayout,
409 585 'empty_cart_layout' => $emptyCartLayout,
410 586 'cart_driver' => Helper::getCartDriver(),
411 587 'cart_image' => Vite::getAssetUrl('images/cart.svg'),
@@ -413,11 +589,13 @@
413 589 'has_active_cart' => !!$cart,
414 590 'is_drawer_hidden' => CartLoader::shouldHideCartDrawer(),
415 591 'is_admin_bar_showing' => is_admin_bar_showing(),
416 592 'cart_item_count' => $isInstantCheckout ? 0 : ($cart ? count($cart->cart_data ?? []) : 0),
593 + 'cart_total_quantity' => $isInstantCheckout ? 0 : ($cart ? array_sum(array_map(function ($item) { return (int) ($item['quantity'] ?? 1); }, $cart->cart_data ?? [])) : 0),
417 594 ],
418 595 'fluentcart_utm_vars' => [
419 - 'allowed_keys' => UtmHelper::allowedUtmParameterKey()
596 + 'allowed_keys' => UtmHelper::allowedUtmParameterKey(),
597 + 'internal_domains' => UtmHelper::getInternalDomains()
420 598 ]
421 599 ]);
422 600
423 601 Vite::enqueueStyle(
@@ -512,8 +690,16 @@
512 690 if ($hasInstantCheckout || $hasModalCheckout) {
513 691 $isInstantCheckout = 'yes';
514 692 }
515 693
694 + $checkoutErrorNotices = Arr::get($cart->checkout_data, '__checkout_error_notices', []);
695 + if ($checkoutErrorNotices) {
696 + $checkoutData = is_array($cart->checkout_data) ? $cart->checkout_data : [];
697 + unset($checkoutData['__checkout_error_notices']);
698 + $cart->checkout_data = $checkoutData;
699 + $cart->save();
700 + }
701 +
516 702 $data = [
517 703 'fluentcart_checkout_vars' => [
518 704 'rest' => Helper::getRestInfo(),
519 705 'ajaxurl' => admin_url('admin-ajax.php'),
@@ -519,8 +705,9 @@
519 705 'ajaxurl' => admin_url('admin-ajax.php'),
520 706 'is_all_digital' => !$cart->requireShipping(),
521 707 'is_cart_locked' => $cart->checkout_data['is_locked'] ?? 'no',
522 708 'disable_coupons' => $cart->checkout_data['disable_coupons'] ?? 'no',
709 + 'notices' => array_values($checkoutErrorNotices),
523 710 'payment_methods_with_custom_checkout_buttons' => apply_filters('fluent_cart/payment_methods_with_custom_checkout_buttons', []),
524 711 'tax_settings' => (new TaxModule())->getSettings(),
525 712 'submit_button' => [
526 713 'text' => __('Place Order', 'fluent-cart'),