PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 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 All 51 releases
← All changes | app/Hooks/actions.php +102 -5 1.3.21 → 1.7.1 View file →
@@ -25,18 +25,25 @@
25 25 (new \FluentCart\App\Hooks\Handlers\RetentionSnapshotHandler)->register();
26 26 (new \FluentCart\App\Hooks\Handlers\ReminderHandler)->register();
27 27
28 28 (new \FluentCart\App\Hooks\Handlers\ShortCodes\ShopAppHandler)->register();
29 -(new \FluentCart\App\Hooks\Handlers\ExportHandler)->register();
29 +(new \FluentCart\App\Hooks\Handlers\ShortCodes\OrderReviewShortCode)->register();
30 30
31 31 (new FluentCart\App\Hooks\Handlers\CustomCheckout\CustomCheckout())->register();
32 32
33 33 // Tax Module Init
34 -(new \FluentCart\App\Modules\Tax\TaxModule())->register();
34 +add_action('init', function () {
35 + (new \FluentCart\App\Modules\Tax\TaxModule())->register();
36 +}, 1);
35 37
36 38 // Turnstile Module Init
37 39 (new \FluentCart\App\Modules\Turnstile\TurnstileInit())->register(\FluentCart\App\App::getInstance());
38 40
41 +// Reviews Module Init
42 +(new \FluentCart\App\Modules\Reviews\ReviewModule())->register();
43 +
44 +(new \FluentCart\App\Modules\FluentPlayer\FluentPlayerAdminAssets())->register();
45 +
39 46 // Register Pro Gateways Promo
40 47 (new \FluentCart\App\Hooks\Handlers\PromoGatewaysHandler())->register();
41 48
42 49 // Register Addon Gateways
@@ -73,8 +80,9 @@
73 80 \FluentCart\App\Hooks\Handlers\BlockEditors\Buttons\BuyNowButtonBlockEditor::register();
74 81 \FluentCart\App\Hooks\Handlers\BlockEditors\Buttons\AddToCartButtonBlockEditor::register();
75 82
76 83 \FluentCart\App\Hooks\Handlers\BlockEditors\MiniCartBlockEditor::register();
84 +\FluentCart\App\Hooks\Handlers\BlockEditors\Cart\CartBlockEditor::register();
77 85 \FluentCart\App\Hooks\Handlers\BlockEditors\ProductCategoriesListBlockEditor::register();
78 86 \FluentCart\App\Hooks\Handlers\BlockEditors\RelatedProduct\RelatedProductBlockEditor::register();
79 87 \FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\DirectCheckoutShortcode::register();
80 88 \FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\AddToCartShortcode::register();
@@ -81,11 +89,13 @@
81 89 \FluentCart\App\Hooks\Handlers\ShortCodes\MiniCartShortcode::register();
82 90 \FluentCart\App\Hooks\Handlers\ShortCodes\ProductCategoriesListShortcode::register();
83 91 \FluentCart\App\Hooks\Handlers\ShortCodes\ProductTitleShortCode::register();
84 92 \FluentCart\App\Hooks\Handlers\ShortCodes\ProductImageShortCode::register();
93 +\FluentCart\App\Hooks\Handlers\ShortCodes\SingleProductShortCode::register();
85 94
86 95
87 96 \FluentCart\App\Hooks\Handlers\BlockEditors\ProductSkuBlockEditor::register();
97 +\FluentCart\App\Hooks\Handlers\BlockEditors\ProductPackageDescriptionBlockEditor::register();
88 98 \FluentCart\App\Hooks\Handlers\BlockEditors\StoreLogoBlockEditor::register();
89 99 \FluentCart\App\Hooks\Handlers\ShortCodes\StoreLogoShortCode::register();
90 100 \FluentCart\App\Hooks\Handlers\BlockEditors\CustomerDashboardButtonBlockEditor::register();
91 101 \FluentCart\App\Hooks\Handlers\ShortCodes\CustomerDashboardButtonShortcode::register();
@@ -97,8 +107,26 @@
97 107 \FluentCart\App\Hooks\Handlers\BlockEditors\StockBlock::register();
98 108 \FluentCart\App\Hooks\Handlers\BlockEditors\SoldOutBadgeBlockEditor::register();
99 109 }
100 110
111 +// Registered whether or not the reviews module is active: the block ships
112 +// inside the Shop App, Product Info and Related Products card templates, so
113 +// an unregistered block would greet the editor with "your site doesn't
114 +// include support for this block" on every card. With the module off it
115 +// stays out of the inserter and renders nothing, in the editor and on the
116 +// page — see ProductRatingBlockEditor.
117 +\FluentCart\App\Hooks\Handlers\BlockEditors\ProductRatingBlockEditor::register();
118 +
119 +if (\FluentCart\Api\ModuleSettings::isActive('reviews') && class_exists(\FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewsBlockEditor::class)) {
120 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewsBlockEditor::register();
121 + \FluentCart\App\Hooks\Handlers\BlockEditors\WriteAReviewButtonBlockEditor::register();
122 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewFormBlockEditor::register();
123 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewSummaryBlockEditor::register();
124 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewList\ProductReviewListBlockEditor::register();
125 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewSummaryGroupBlockEditor::register();
126 + \FluentCart\App\Hooks\Handlers\ShortCodes\ProductReviewsShortCode::register();
127 +}
128 +
101 129 (new \FluentCart\App\Hooks\Cart\CartLoader)->register();
102 130 (new \FluentCart\App\Hooks\Handlers\GlobalPaymentHandler)->register();
103 131
104 132 \FluentCart\App\Http\Routes\WebRoutes::register();
@@ -129,13 +157,21 @@
129 157 return;
130 158 }
131 159
132 160 $order = \FluentCart\App\Models\Order::find($orderId);
133 - if (!$order || $order->payment_status !== 'paid' || !$order->getMeta('action_scheduler_id')) {
161 + if (!$order || $order->payment_status !== 'paid') {
134 162 return;
135 163 }
136 164
137 - $order->deleteMeta('action_scheduler_id');
165 + // Claim the run by deleting the meta and branching on the affected-row count.
166 + // The Action Scheduler worker and the receipt-page AJAX nudge
167 + // (IntegrationEventListener::runOrderActionsAjax) can arrive together, so a
168 + // read-then-delete would let both pass the guard and fire order_paid_done
169 + // twice. A single DELETE is atomic: MySQL hands the row to exactly one
170 + // caller, and the loser sees 0 rows and returns.
171 + if (!$order->deleteMeta('action_scheduler_id')) {
172 + return;
173 + }
138 174
139 175 $transaction = \FluentCart\App\Models\OrderTransaction::query()
140 176 ->where('order_id', $order->id)
141 177 ->where('status', \FluentCart\App\Helpers\Status::TRANSACTION_SUCCEEDED)
@@ -158,11 +194,65 @@
158 194 do_action('fluent_cart/order_paid_done', $eventData);
159 195
160 196 };
161 197 add_action('fluent_cart/order_paid_async_private_handle', $orderPaidAsyncHandler, 1, 1);
162 -add_action('fluent_cart/order_paid_ansyc_private_handle', $orderPaidAsyncHandler, 1, 1);
163 198
199 +// The settled half of a review approval — see ReviewApproved::afterDispatch().
200 +// Runs off the request, so it trusts nothing it was told: the review is
201 +// re-read, and only one that is still an approved top-level review is
202 +// announced. Deleted or held back since, nothing fires.
203 +$reviewApprovedAsyncHandler = function ($data) {
204 + $reviewId = (int) \FluentCart\Framework\Support\Arr::get($data, 'review_id');
164 205
206 + if (!$reviewId) {
207 + return;
208 + }
209 +
210 + $review = \FluentCart\App\Models\ProductReview::query()->with('product')->find($reviewId);
211 +
212 + if (!$review || $review->parent_id || $review->status !== \FluentCart\App\Helpers\Status::REVIEW_APPROVED) {
213 + return;
214 + }
215 +
216 + do_action('fluent_cart/review_approved_done', [
217 + 'review' => $review,
218 + 'product' => $review->product,
219 + ]);
220 +};
221 +add_action('fluent_cart/review_approved_async_private_handle', $reviewApprovedAsyncHandler, 1, 1);
222 +
223 +// The settled half of a store reply — see ReviewReplied::afterDispatch().
224 +// Announced only while both halves are published: the reply itself, and the
225 +// review it answers. A reply to a review nobody can see is not a
226 +// conversation the reviewer can be pointed at.
227 +$reviewRepliedAsyncHandler = function ($data) {
228 + $replyId = (int) \FluentCart\Framework\Support\Arr::get($data, 'reply_id');
229 +
230 + if (!$replyId) {
231 + return;
232 + }
233 +
234 + $reply = \FluentCart\App\Models\ProductReview::query()->with('product')->find($replyId);
235 +
236 + if (!$reply || !$reply->parent_id || !$reply->is_admin_reply || $reply->status !== \FluentCart\App\Helpers\Status::REVIEW_APPROVED) {
237 + return;
238 + }
239 +
240 + $review = \FluentCart\App\Models\ProductReview::query()->find((int) $reply->parent_id);
241 +
242 + if (!$review || $review->parent_id || $review->status !== \FluentCart\App\Helpers\Status::REVIEW_APPROVED) {
243 + return;
244 + }
245 +
246 + do_action('fluent_cart/review_replied_done', [
247 + 'reply' => $reply,
248 + 'review' => $review,
249 + 'product' => $reply->product,
250 + ]);
251 +};
252 +add_action('fluent_cart/review_replied_async_private_handle', $reviewRepliedAsyncHandler, 1, 1);
253 +
254 +
165 255 //
166 256 //$app->addAction('fluent_cart/orders_filter_customer', function ($query, $filters) {
167 257 // return (new \FluentCart\App\Models\Order)->buildCustomerFilterQuery($query, $filters);
168 258 //}, 10, 2);
@@ -174,8 +264,15 @@
174 264 }
175 265
176 266 \FluentCart\App\Modules\Subscriptions\SubscriptionModule::register();
177 267 \FluentCart\App\Modules\Shipping\ShippingModule::register();
268 +\FluentCart\App\Modules\StoreManagedRenewal\RenewalModule::register();
269 +(new \FluentCart\App\Hooks\Handlers\AttributesHandler())->register();
270 +(new \FluentCart\App\Hooks\Handlers\AdvancedVariationHandler())->register();
271 +
272 +// MCP (Model Context Protocol) — operator AI tools. Ships OFF; enabled in
273 +// Settings → Features & addon → MCP. See MCPInit::boot() for the bootstrap.
274 +\FluentCart\App\Modules\MCP\MCPInit::boot();
178 275
179 276 $app->ready(function () use ($app) {
180 277 \FluentCart\App\Models\Connection\ConnectionManager::connect($this->app);
181 278 });