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 +86 -4 1.4.0 → 1.7.0 View file →
@@ -25,9 +25,9 @@
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
@@ -37,8 +37,13 @@
37 37
38 38 // Turnstile Module Init
39 39 (new \FluentCart\App\Modules\Turnstile\TurnstileInit())->register(\FluentCart\App\App::getInstance());
40 40
41 +// Reviews Module Init
42 +(new \FluentCart\App\Modules\Reviews\ReviewModule())->register();
43 +
44 +(new \FluentCart\App\Modules\FluentPlayer\FluentPlayerAdminAssets())->register();
45 +
41 46 // Register Pro Gateways Promo
42 47 (new \FluentCart\App\Hooks\Handlers\PromoGatewaysHandler())->register();
43 48
44 49 // Register Addon Gateways
@@ -75,8 +80,9 @@
75 80 \FluentCart\App\Hooks\Handlers\BlockEditors\Buttons\BuyNowButtonBlockEditor::register();
76 81 \FluentCart\App\Hooks\Handlers\BlockEditors\Buttons\AddToCartButtonBlockEditor::register();
77 82
78 83 \FluentCart\App\Hooks\Handlers\BlockEditors\MiniCartBlockEditor::register();
84 +\FluentCart\App\Hooks\Handlers\BlockEditors\Cart\CartBlockEditor::register();
79 85 \FluentCart\App\Hooks\Handlers\BlockEditors\ProductCategoriesListBlockEditor::register();
80 86 \FluentCart\App\Hooks\Handlers\BlockEditors\RelatedProduct\RelatedProductBlockEditor::register();
81 87 \FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\DirectCheckoutShortcode::register();
82 88 \FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\AddToCartShortcode::register();
@@ -101,8 +107,19 @@
101 107 \FluentCart\App\Hooks\Handlers\BlockEditors\StockBlock::register();
102 108 \FluentCart\App\Hooks\Handlers\BlockEditors\SoldOutBadgeBlockEditor::register();
103 109 }
104 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 +
105 122 (new \FluentCart\App\Hooks\Cart\CartLoader)->register();
106 123 (new \FluentCart\App\Hooks\Handlers\GlobalPaymentHandler)->register();
107 124
108 125 \FluentCart\App\Http\Routes\WebRoutes::register();
@@ -133,13 +150,21 @@
133 150 return;
134 151 }
135 152
136 153 $order = \FluentCart\App\Models\Order::find($orderId);
137 - if (!$order || $order->payment_status !== 'paid' || !$order->getMeta('action_scheduler_id')) {
154 + if (!$order || $order->payment_status !== 'paid') {
138 155 return;
139 156 }
140 157
141 - $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 + }
142 167
143 168 $transaction = \FluentCart\App\Models\OrderTransaction::query()
144 169 ->where('order_id', $order->id)
145 170 ->where('status', \FluentCart\App\Helpers\Status::TRANSACTION_SUCCEEDED)
@@ -162,11 +187,65 @@
162 187 do_action('fluent_cart/order_paid_done', $eventData);
163 188
164 189 };
165 190 add_action('fluent_cart/order_paid_async_private_handle', $orderPaidAsyncHandler, 1, 1);
166 -add_action('fluent_cart/order_paid_ansyc_private_handle', $orderPaidAsyncHandler, 1, 1);
167 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');
168 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 +
169 248 //
170 249 //$app->addAction('fluent_cart/orders_filter_customer', function ($query, $filters) {
171 250 // return (new \FluentCart\App\Models\Order)->buildCustomerFilterQuery($query, $filters);
172 251 //}, 10, 2);
@@ -178,8 +257,11 @@
178 257 }
179 258
180 259 \FluentCart\App\Modules\Subscriptions\SubscriptionModule::register();
181 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();
182 264
183 265 // MCP (Model Context Protocol) — operator AI tools. Ships OFF; enabled in
184 266 // Settings → Features & addon → MCP. See MCPInit::boot() for the bootstrap.
185 267 \FluentCart\App\Modules\MCP\MCPInit::boot();