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 +94 -5 1.3.26 → 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,8 +89,9 @@
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();
88 97 \FluentCart\App\Hooks\Handlers\BlockEditors\ProductPackageDescriptionBlockEditor::register();
@@ -98,8 +107,19 @@
98 107 \FluentCart\App\Hooks\Handlers\BlockEditors\StockBlock::register();
99 108 \FluentCart\App\Hooks\Handlers\BlockEditors\SoldOutBadgeBlockEditor::register();
100 109 }
101 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 +
102 122 (new \FluentCart\App\Hooks\Cart\CartLoader)->register();
103 123 (new \FluentCart\App\Hooks\Handlers\GlobalPaymentHandler)->register();
104 124
105 125 \FluentCart\App\Http\Routes\WebRoutes::register();
@@ -130,13 +150,21 @@
130 150 return;
131 151 }
132 152
133 153 $order = \FluentCart\App\Models\Order::find($orderId);
134 - if (!$order || $order->payment_status !== 'paid' || !$order->getMeta('action_scheduler_id')) {
154 + if (!$order || $order->payment_status !== 'paid') {
135 155 return;
136 156 }
137 157
138 - $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 + }
139 167
140 168 $transaction = \FluentCart\App\Models\OrderTransaction::query()
141 169 ->where('order_id', $order->id)
142 170 ->where('status', \FluentCart\App\Helpers\Status::TRANSACTION_SUCCEEDED)
@@ -159,11 +187,65 @@
159 187 do_action('fluent_cart/order_paid_done', $eventData);
160 188
161 189 };
162 190 add_action('fluent_cart/order_paid_async_private_handle', $orderPaidAsyncHandler, 1, 1);
163 -add_action('fluent_cart/order_paid_ansyc_private_handle', $orderPaidAsyncHandler, 1, 1);
164 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');
165 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 +
166 248 //
167 249 //$app->addAction('fluent_cart/orders_filter_customer', function ($query, $filters) {
168 250 // return (new \FluentCart\App\Models\Order)->buildCustomerFilterQuery($query, $filters);
169 251 //}, 10, 2);
@@ -175,8 +257,15 @@
175 257 }
176 258
177 259 \FluentCart\App\Modules\Subscriptions\SubscriptionModule::register();
178 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();
179 268
180 269 $app->ready(function () use ($app) {
181 270 \FluentCart\App\Models\Connection\ConnectionManager::connect($this->app);
182 271 });