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/Hooks/actions.php +95 -5 1.3.21 → 1.7.0 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,19 @@
97 107 \FluentCart\App\Hooks\Handlers\BlockEditors\StockBlock::register();
98 108 \FluentCart\App\Hooks\Handlers\BlockEditors\SoldOutBadgeBlockEditor::register();
99 109 }
100 110
111 +if (\FluentCart\Api\ModuleSettings::isActive('reviews') && class_exists(\FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewsBlockEditor::class)) {
112 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewsBlockEditor::register();
113 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductRatingBlockEditor::register();
114 + \FluentCart\App\Hooks\Handlers\BlockEditors\WriteAReviewButtonBlockEditor::register();
115 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewFormBlockEditor::register();
116 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewSummaryBlockEditor::register();
117 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewList\ProductReviewListBlockEditor::register();
118 + \FluentCart\App\Hooks\Handlers\BlockEditors\ProductReviewSummaryGroupBlockEditor::register();
119 + \FluentCart\App\Hooks\Handlers\ShortCodes\ProductReviewsShortCode::register();
120 +}
121 +
101 122 (new \FluentCart\App\Hooks\Cart\CartLoader)->register();
102 123 (new \FluentCart\App\Hooks\Handlers\GlobalPaymentHandler)->register();
103 124
104 125 \FluentCart\App\Http\Routes\WebRoutes::register();
@@ -129,13 +150,21 @@
129 150 return;
130 151 }
131 152
132 153 $order = \FluentCart\App\Models\Order::find($orderId);
133 - if (!$order || $order->payment_status !== 'paid' || !$order->getMeta('action_scheduler_id')) {
154 + if (!$order || $order->payment_status !== 'paid') {
134 155 return;
135 156 }
136 157
137 - $order->deleteMeta('action_scheduler_id');
158 + // Claim the run by deleting the meta and branching on the affected-row count.
159 + // The Action Scheduler worker and the receipt-page AJAX nudge
160 + // (IntegrationEventListener::runOrderActionsAjax) can arrive together, so a
161 + // read-then-delete would let both pass the guard and fire order_paid_done
162 + // twice. A single DELETE is atomic: MySQL hands the row to exactly one
163 + // caller, and the loser sees 0 rows and returns.
164 + if (!$order->deleteMeta('action_scheduler_id')) {
165 + return;
166 + }
138 167
139 168 $transaction = \FluentCart\App\Models\OrderTransaction::query()
140 169 ->where('order_id', $order->id)
141 170 ->where('status', \FluentCart\App\Helpers\Status::TRANSACTION_SUCCEEDED)
@@ -158,11 +187,65 @@
158 187 do_action('fluent_cart/order_paid_done', $eventData);
159 188
160 189 };
161 190 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 191
192 +// The settled half of a review approval — see ReviewApproved::afterDispatch().
193 +// Runs off the request, so it trusts nothing it was told: the review is
194 +// re-read, and only one that is still an approved top-level review is
195 +// announced. Deleted or held back since, nothing fires.
196 +$reviewApprovedAsyncHandler = function ($data) {
197 + $reviewId = (int) \FluentCart\Framework\Support\Arr::get($data, 'review_id');
164 198
199 + if (!$reviewId) {
200 + return;
201 + }
202 +
203 + $review = \FluentCart\App\Models\ProductReview::query()->with('product')->find($reviewId);
204 +
205 + if (!$review || $review->parent_id || $review->status !== \FluentCart\App\Helpers\Status::REVIEW_APPROVED) {
206 + return;
207 + }
208 +
209 + do_action('fluent_cart/review_approved_done', [
210 + 'review' => $review,
211 + 'product' => $review->product,
212 + ]);
213 +};
214 +add_action('fluent_cart/review_approved_async_private_handle', $reviewApprovedAsyncHandler, 1, 1);
215 +
216 +// The settled half of a store reply — see ReviewReplied::afterDispatch().
217 +// Announced only while both halves are published: the reply itself, and the
218 +// review it answers. A reply to a review nobody can see is not a
219 +// conversation the reviewer can be pointed at.
220 +$reviewRepliedAsyncHandler = function ($data) {
221 + $replyId = (int) \FluentCart\Framework\Support\Arr::get($data, 'reply_id');
222 +
223 + if (!$replyId) {
224 + return;
225 + }
226 +
227 + $reply = \FluentCart\App\Models\ProductReview::query()->with('product')->find($replyId);
228 +
229 + if (!$reply || !$reply->parent_id || !$reply->is_admin_reply || $reply->status !== \FluentCart\App\Helpers\Status::REVIEW_APPROVED) {
230 + return;
231 + }
232 +
233 + $review = \FluentCart\App\Models\ProductReview::query()->find((int) $reply->parent_id);
234 +
235 + if (!$review || $review->parent_id || $review->status !== \FluentCart\App\Helpers\Status::REVIEW_APPROVED) {
236 + return;
237 + }
238 +
239 + do_action('fluent_cart/review_replied_done', [
240 + 'reply' => $reply,
241 + 'review' => $review,
242 + 'product' => $reply->product,
243 + ]);
244 +};
245 +add_action('fluent_cart/review_replied_async_private_handle', $reviewRepliedAsyncHandler, 1, 1);
246 +
247 +
165 248 //
166 249 //$app->addAction('fluent_cart/orders_filter_customer', function ($query, $filters) {
167 250 // return (new \FluentCart\App\Models\Order)->buildCustomerFilterQuery($query, $filters);
168 251 //}, 10, 2);
@@ -174,8 +257,15 @@
174 257 }
175 258
176 259 \FluentCart\App\Modules\Subscriptions\SubscriptionModule::register();
177 260 \FluentCart\App\Modules\Shipping\ShippingModule::register();
261 +\FluentCart\App\Modules\StoreManagedRenewal\RenewalModule::register();
262 +(new \FluentCart\App\Hooks\Handlers\AttributesHandler())->register();
263 +(new \FluentCart\App\Hooks\Handlers\AdvancedVariationHandler())->register();
264 +
265 +// MCP (Model Context Protocol) — operator AI tools. Ships OFF; enabled in
266 +// Settings → Features & addon → MCP. See MCPInit::boot() for the bootstrap.
267 +\FluentCart\App\Modules\MCP\MCPInit::boot();
178 268
179 269 $app->ready(function () use ($app) {
180 270 \FluentCart\App\Models\Connection\ConnectionManager::connect($this->app);
181 271 });