PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
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 1.3.19 trunk All 48 releases
← All changes | app/Helpers/Helper.php +215 -119 1.6.1 → 1.6.5 View file →
@@ -11,8 +11,9 @@
11 11 use FluentCart\App\Models\Customer;
12 12 use FluentCart\App\Models\ProductVariation;
13 13 use FluentCart\App\Models\User;
14 14 use FluentCart\App\Services\DateTime\DateTime;
15 +use FluentCart\App\Services\Payments\PaymentHelper;
15 16 use FluentCart\App\Services\Localization\LocalizationManager;
16 17 use FluentCart\App\Services\Translations\TransStrings;
17 18 use FluentCart\App\Services\URL;
18 19 use FluentCart\Framework\Http\URL as BaseUrl;
@@ -278,17 +279,15 @@
278 279 }
279 280
280 281 public static function getOrderStatuses()
281 282 {
282 - $statuses = apply_filters_deprecated('fluent-cart/order_statuses', [
283 - [
284 - 'on-hold' => __('On Hold', 'fluent-cart'),
285 - 'processing' => __('Processing', 'fluent-cart'),
286 - 'completed' => __('Completed', 'fluent-cart'),
287 - //'archived' => __('Archived', 'fluent-cart'),
288 - 'cancelled' => __('Cancelled', 'fluent-cart'),
289 - ], []
290 - ], '1.3.16', 'fluent_cart/order_statuses', 'Use fluent_cart/order_statuses instead of fluent-cart/order_statuses. It will be removed in v1.4.3.');
283 + $statuses = [
284 + 'on-hold' => __('On Hold', 'fluent-cart'),
285 + 'processing' => __('Processing', 'fluent-cart'),
286 + 'completed' => __('Completed', 'fluent-cart'),
287 + //'archived' => __('Archived', 'fluent-cart'),
288 + 'cancelled' => __('Cancelled', 'fluent-cart'),
289 + ];
291 290
292 291 return apply_filters('fluent_cart/order_statuses', $statuses, []);
293 292 }
294 293
@@ -293,17 +292,15 @@
293 292 }
294 293
295 294 public static function getEditableOrderStatuses()
296 295 {
297 - $statuses = apply_filters_deprecated('fluent-cart/editable_order_statuses', [
298 - [
299 - 'on-hold' => __('On Hold', 'fluent-cart'),
300 - 'processing' => __('Processing', 'fluent-cart'),
301 - 'completed' => __('Completed', 'fluent-cart'),
302 - // 'archived' => __('Archived', 'fluent-cart'),
303 - 'cancelled' => __('Cancelled', 'fluent-cart')
304 - ], []
305 - ], '1.3.16', 'fluent_cart/editable_order_statuses', 'Use fluent_cart/editable_order_statuses instead of fluent-cart/editable_order_statuses. It will be removed in v1.4.3.');
296 + $statuses = [
297 + 'on-hold' => __('On Hold', 'fluent-cart'),
298 + 'processing' => __('Processing', 'fluent-cart'),
299 + 'completed' => __('Completed', 'fluent-cart'),
300 + // 'archived' => __('Archived', 'fluent-cart'),
301 + 'cancelled' => __('Cancelled', 'fluent-cart')
302 + ];
306 303
307 304 return apply_filters('fluent_cart/editable_order_statuses', $statuses, []);
308 305 }
309 306
@@ -308,14 +305,12 @@
308 305 }
309 306
310 307 public static function getEditableCustomerStatuses()
311 308 {
312 - $statuses = apply_filters_deprecated('fluent-cart/editable_customer_statuses', [
313 - [
314 - 'active' => __('Active', 'fluent-cart'),
315 - 'inactive' => __('Inactive', 'fluent-cart'),
316 - ], []
317 - ], '1.3.16', 'fluent_cart/editable_customer_statuses', 'Use fluent_cart/editable_customer_statuses instead of fluent-cart/editable_customer_statuses. It will be removed in v1.4.3.');
309 + $statuses = [
310 + 'active' => __('Active', 'fluent-cart'),
311 + 'inactive' => __('Inactive', 'fluent-cart'),
312 + ];
318 313
319 314 return apply_filters('fluent_cart/editable_customer_statuses', $statuses, []);
320 315 }
321 316
@@ -320,16 +315,14 @@
320 315 }
321 316
322 317 public static function getShippingStatuses()
323 318 {
324 - $statuses = apply_filters_deprecated('fluent-cart/shipping_statuses', [
325 - [
326 - 'unshipped' => __('Unshipped', 'fluent-cart'),
327 - 'shipped' => __('Shipped', 'fluent-cart'),
328 - 'delivered' => __('Delivered', 'fluent-cart'),
329 - 'unshippable' => __('Unshippable', 'fluent-cart'),
330 - ], []
331 - ], '1.3.16', 'fluent_cart/shipping_statuses', 'Use fluent_cart/shipping_statuses instead of fluent-cart/shipping_statuses. It will be removed in v1.4.3.');
319 + $statuses = [
320 + 'unshipped' => __('Unshipped', 'fluent-cart'),
321 + 'shipped' => __('Shipped', 'fluent-cart'),
322 + 'delivered' => __('Delivered', 'fluent-cart'),
323 + 'unshippable' => __('Unshippable', 'fluent-cart'),
324 + ];
332 325
333 326 return apply_filters('fluent_cart/shipping_statuses', $statuses, []);
334 327 }
335 328
@@ -334,16 +327,14 @@
334 327 }
335 328
336 329 public static function getEditableShippingStatuses()
337 330 {
338 - $statuses = apply_filters_deprecated('fluent-cart/editable_order_statuses', [
339 - [
340 - 'unshipped' => __('Unshipped', 'fluent-cart'),
341 - 'shipped' => __('Shipped', 'fluent-cart'),
342 - 'delivered' => __('Delivered', 'fluent-cart'),
343 - 'unshippable' => __('Unshippable', 'fluent-cart'),
344 - ], []
345 - ], '1.3.16', 'fluent_cart/editable_shipping_statuses', 'Use fluent_cart/editable_shipping_statuses instead of fluent-cart/editable_order_statuses. It will be removed in v1.4.3.');
331 + $statuses = [
332 + 'unshipped' => __('Unshipped', 'fluent-cart'),
333 + 'shipped' => __('Shipped', 'fluent-cart'),
334 + 'delivered' => __('Delivered', 'fluent-cart'),
335 + 'unshippable' => __('Unshippable', 'fluent-cart'),
336 + ];
346 337
347 338 return apply_filters('fluent_cart/editable_shipping_statuses', $statuses, []);
348 339 }
349 340
@@ -373,18 +364,16 @@
373 364 }
374 365
375 366 public static function getTransactionStatuses($withLabel = true)
376 367 {
377 - $statuses = apply_filters_deprecated('fluent-cart/transaction_statuses', [
378 - [
379 - 'pending' => __('Pending', 'fluent-cart'),
380 - 'paid' => __('Paid', 'fluent-cart'),
381 - 'require_capture' => __('Authorized (Require Capture)', 'fluent-cart'),
382 - 'failed' => __('Failed', 'fluent-cart'),
383 - 'refunded' => __('Refunded', 'fluent-cart'),
384 - 'active' => __('Active', 'fluent-cart'),
385 - ], []
386 - ], '1.3.16', 'fluent_cart/transaction_statuses', 'Use fluent_cart/transaction_statuses instead of fluent-cart/transaction_statuses. It will be removed in v1.4.3.');
368 + $statuses = [
369 + 'pending' => __('Pending', 'fluent-cart'),
370 + 'paid' => __('Paid', 'fluent-cart'),
371 + 'require_capture' => __('Authorized (Require Capture)', 'fluent-cart'),
372 + 'failed' => __('Failed', 'fluent-cart'),
373 + 'refunded' => __('Refunded', 'fluent-cart'),
374 + 'active' => __('Active', 'fluent-cart'),
375 + ];
387 376
388 377 $statuses = apply_filters('fluent_cart/transaction_statuses', $statuses, []);
389 378
390 379 if ($withLabel) {
@@ -395,16 +384,14 @@
395 384 }
396 385
397 386 public static function getEditableTransactionStatuses($withLabel = true)
398 387 {
399 - $statuses = apply_filters_deprecated('fluent-cart/editable_transaction_statuses', [
400 - [
401 - 'pending' => __('Pending', 'fluent-cart'),
402 - 'paid' => __('Paid', 'fluent-cart'),
403 - 'failed' => __('Failed', 'fluent-cart'),
404 - 'refunded' => __('Refunded', 'fluent-cart'),
405 - ], []
406 - ], '1.3.16', 'fluent_cart/editable_transaction_statuses', 'Use fluent_cart/editable_transaction_statuses instead of fluent-cart/editable_transaction_statuses. It will be removed in v1.4.3.');
388 + $statuses = [
389 + 'pending' => __('Pending', 'fluent-cart'),
390 + 'paid' => __('Paid', 'fluent-cart'),
391 + 'failed' => __('Failed', 'fluent-cart'),
392 + 'refunded' => __('Refunded', 'fluent-cart'),
393 + ];
407 394
408 395 $statuses = apply_filters('fluent_cart/editable_transaction_statuses', $statuses, []);
409 396
410 397 if ($withLabel) {
@@ -413,21 +400,8 @@
413 400
414 401 return array_keys($statuses);
415 402 }
416 403
417 - public static function loadSpoutLib()
418 - {
419 - static $loaded;
420 -
421 - if ($loaded) {
422 - return $loaded;
423 - }
424 -
425 - require_once FLUENTCART_PLUGIN_PATH . 'app/Services/Libs/Spout/Autoloader/autoload.php';
426 -
427 - return true;
428 - }
429 -
430 404 public static function productStatuses($withLabel = true): array
431 405 {
432 406 $statues = [
433 407 'publish' => __('Publish', 'fluent-cart'),
@@ -635,10 +609,12 @@
635 609 if (!$user) {
636 610 return null;
637 611 }
638 612
613 + // Identity only — see CustomerResource::getCurrentCustomer() for why an
614 + // email match must never reach a customer row.
639 615 return Customer::query()->where('user_id', $user->ID)
640 - ->orWhere('email', $user->user_email)
616 + ->orderBy('id', 'ASC')
641 617 ->first();
642 618 }
643 619
644 620 /**
@@ -666,27 +642,25 @@
666 642 }
667 643
668 644 public static function getAvailableCurrencyList()
669 645 {
670 - $currencies = apply_filters_deprecated('fluent-cart/available_currencies', [
671 - [
672 - 'BDT' => [
673 - "label" => __('Bangladeshi Taka', 'fluent-cart'),
674 - "value" => 'BDT',
675 - "symbol" => '৳',
676 - ],
677 - 'USD' => [
678 - "label" => __('United State Dollar', 'fluent-cart'),
679 - "value" => 'USD',
680 - "symbol" => '$',
681 - ],
682 - 'GBP' => [
683 - "label" => __('United Kingdom', 'fluent-cart'),
684 - "value" => 'GBP',
685 - "symbol" => '£',
686 - ],
687 - ], []
688 - ], '1.3.16', 'fluent_cart/available_currencies', 'Use fluent_cart/available_currencies instead of fluent-cart/available_currencies. It will be removed in v1.4.3.');
646 + $currencies = [
647 + 'BDT' => [
648 + "label" => __('Bangladeshi Taka', 'fluent-cart'),
649 + "value" => 'BDT',
650 + "symbol" => '৳',
651 + ],
652 + 'USD' => [
653 + "label" => __('United State Dollar', 'fluent-cart'),
654 + "value" => 'USD',
655 + "symbol" => '$',
656 + ],
657 + 'GBP' => [
658 + "label" => __('United Kingdom', 'fluent-cart'),
659 + "value" => 'GBP',
660 + "symbol" => '£',
661 + ],
662 + ];
689 663
690 664 return apply_filters('fluent_cart/available_currencies', $currencies, []);
691 665 }
692 666
@@ -1033,15 +1007,13 @@
1033 1007 }
1034 1008
1035 1009 public static function getCouponStatuses()
1036 1010 {
1037 - $statuses = apply_filters_deprecated('fluent-cart/coupon_statuses', [
1038 - [
1039 - 'active' => __('Active', 'fluent-cart'),
1040 - 'expired' => __('Expired', 'fluent-cart'),
1041 - 'disabled' => __('Disabled', 'fluent-cart'),
1042 - ], []
1043 - ], '1.3.16', 'fluent_cart/coupon_statuses', 'Use fluent_cart/coupon_statuses instead of fluent-cart/coupon_statuses. It will be removed in v1.4.3.');
1011 + $statuses = [
1012 + 'active' => __('Active', 'fluent-cart'),
1013 + 'expired' => __('Expired', 'fluent-cart'),
1014 + 'disabled' => __('Disabled', 'fluent-cart'),
1015 + ];
1044 1016
1045 1017 return apply_filters('fluent_cart/coupon_statuses', $statuses, []);
1046 1018 }
1047 1019
@@ -1307,8 +1279,150 @@
1307 1279
1308 1280 return !empty($otherInfo) ? $paymentInfo : null;
1309 1281 }
1310 1282
1283 + /**
1284 + * Billing-cycle text for a subscription with a config-defined schedule
1285 + * (see SubscriptionHelper::getBillingSchedule()) — cadences the
1286 + * billing_interval enum cannot express, e.g. "$100 per 3 years on Aug 19".
1287 + * Subscriptions without a schedule keep generateSubscriptionInfo().
1288 + */
1289 + public static function generateScheduleSubscriptionInfo(array $schedule, $otherInfo, $itemPrice, $currencyCode = null): ?string
1290 + {
1291 + if (is_object($otherInfo)) {
1292 + $otherInfo = json_decode(json_encode($otherInfo), true);
1293 + }
1294 +
1295 + $count = max(1, (int) Arr::get($schedule, 'interval', 1));
1296 +
1297 + switch (Arr::get($schedule, 'period')) {
1298 + case 'day':
1299 + $unitLabel = _n('day', 'days', $count, 'fluent-cart');
1300 + break;
1301 + case 'week':
1302 + $unitLabel = _n('week', 'weeks', $count, 'fluent-cart');
1303 + break;
1304 + case 'month':
1305 + $unitLabel = _n('month', 'months', $count, 'fluent-cart');
1306 + break;
1307 + case 'year':
1308 + $unitLabel = _n('year', 'years', $count, 'fluent-cart');
1309 + break;
1310 + default:
1311 + return self::generateSubscriptionInfo($otherInfo, $itemPrice, $currencyCode);
1312 + }
1313 +
1314 + $price = self::toDecimal($itemPrice, true, $currencyCode);
1315 + $recurringDiscountAmount = Arr::get($otherInfo, 'recurring_discounts.amount', 0);
1316 +
1317 + if ($recurringDiscountAmount) {
1318 + $newRecurringAmount = $itemPrice - $recurringDiscountAmount;
1319 + $price = "<del>" . $price . "</del> " . self::toDecimal($newRecurringAmount, true, $currencyCode);
1320 + }
1321 +
1322 + $interval = $count === 1
1323 + ? sprintf(
1324 + /* translators: %s is the interval unit (e.g., day, week, month, year) */
1325 + __('per %s', 'fluent-cart'),
1326 + $unitLabel
1327 + )
1328 + : sprintf(
1329 + /* translators: %1$d is the count number, %2$s is the plural unit name (e.g., days, months, years) */
1330 + __('per %1$d %2$s', 'fluent-cart'),
1331 + $count,
1332 + $unitLabel
1333 + );
1334 +
1335 + if ($anchorText = self::getScheduleAnchorText($schedule['period'], Arr::get($schedule, 'anchor', []))) {
1336 + $interval .= ' ' . $anchorText;
1337 + }
1338 +
1339 + $occurrence = (int) Arr::get($otherInfo, 'times', 0);
1340 +
1341 + if (empty($occurrence)) {
1342 + return sprintf(
1343 + /* translators: %1$s is the price, %2$s is the interval, %3$s is "until cancel" text */
1344 + __('%1$s %2$s %3$s', 'fluent-cart'),
1345 + $price,
1346 + $interval,
1347 + __('until cancel', 'fluent-cart')
1348 + );
1349 + }
1350 +
1351 + return sprintf(
1352 + /* translators: %1$s is the price, %2$s is the interval, %3$s is the occurrence count, %4$s is "cycle(s)" */
1353 + __('%1$s %2$s, for %3$s %4$s', 'fluent-cart'),
1354 + $price,
1355 + $interval,
1356 + $occurrence,
1357 + _n('cycle', 'cycles', $occurrence, 'fluent-cart')
1358 + );
1359 + }
1360 +
1361 + /**
1362 + * Human-readable billing anchor, e.g. "on Friday", "on the 10th",
1363 + * "on the last day", "on Aug 19". Anchor day 31 encodes "last day of
1364 + * the month" (see SubscriptionHelper::getBillingSchedule()).
1365 + */
1366 + private static function getScheduleAnchorText(string $period, $anchor): string
1367 + {
1368 + if (!is_array($anchor) || !$anchor) {
1369 + return '';
1370 + }
1371 +
1372 + if ($period === 'week' && !empty($anchor['weekday'])) {
1373 + $weekdays = [
1374 + 1 => __('Monday', 'fluent-cart'),
1375 + 2 => __('Tuesday', 'fluent-cart'),
1376 + 3 => __('Wednesday', 'fluent-cart'),
1377 + 4 => __('Thursday', 'fluent-cart'),
1378 + 5 => __('Friday', 'fluent-cart'),
1379 + 6 => __('Saturday', 'fluent-cart'),
1380 + 7 => __('Sunday', 'fluent-cart'),
1381 + ];
1382 +
1383 + if (isset($weekdays[$anchor['weekday']])) {
1384 + /* translators: %s is a weekday name, e.g. "on Friday" */
1385 + return sprintf(__('on %s', 'fluent-cart'), $weekdays[$anchor['weekday']]);
1386 + }
1387 +
1388 + return '';
1389 + }
1390 +
1391 + if ($period === 'month' && !empty($anchor['day'])) {
1392 + $day = (int) $anchor['day'];
1393 +
1394 + if ($day === 31) {
1395 + return __('on the last day', 'fluent-cart');
1396 + }
1397 +
1398 + /* translators: %s is an ordinal day of month, e.g. "on the 10th" */
1399 + return sprintf(__('on the %s', 'fluent-cart'), gmdate('jS', gmmktime(12, 0, 0, 1, $day, 2001)));
1400 + }
1401 +
1402 + if ($period === 'year' && (!empty($anchor['day']) || !empty($anchor['month']))) {
1403 + $day = (int) Arr::get($anchor, 'day', 0);
1404 + $month = (int) Arr::get($anchor, 'month', 0);
1405 +
1406 + if ($month && $day) {
1407 + /* translators: %s is a date, e.g. "on Aug 19" */
1408 + // wp_date(), not gmdate(): gmdate() has no locale, so this month
1409 + // name stayed English inside an otherwise translated sentence.
1410 + return sprintf(__('on %s', 'fluent-cart'), wp_date('M', gmmktime(12, 0, 0, $month, 1, 2001), new \DateTimeZone('UTC')) . ' ' . $day);
1411 + }
1412 +
1413 + if ($month) {
1414 + /* translators: %s is a month name, e.g. "in August" */
1415 + return sprintf(__('in %s', 'fluent-cart'), wp_date('F', gmmktime(12, 0, 0, $month, 1, 2001), new \DateTimeZone('UTC')));
1416 + }
1417 +
1418 + /* translators: %s is an ordinal day of month, e.g. "on the 10th" */
1419 + return sprintf(__('on the %s', 'fluent-cart'), gmdate('jS', gmmktime(12, 0, 0, 1, $day, 2001)));
1420 + }
1421 +
1422 + return '';
1423 + }
1424 +
1311 1425 public static function generateSetupFeeInfo($otherInfo, $asArray = false)
1312 1426 {
1313 1427 // Convert to array if it's an object
1314 1428 if (is_object($otherInfo)) {
@@ -1373,9 +1487,8 @@
1373 1487
1374 1488 public static function getCountryList(): array
1375 1489 {
1376 1490 $options = App::getInstance('localization')->countriesOptions();
1377 - $options = apply_filters_deprecated('fluent-cart/util/countries', [$options, []], '1.3.16', 'fluent_cart/util/countries', 'Use fluent_cart/util/countries instead of fluent-cart/util/countries. It will be removed in v1.4.3.');
1378 1491
1379 1492 return apply_filters('fluent_cart/util/countries', $options, []);
1380 1493 }
1381 1494
@@ -1815,26 +1928,9 @@
1815 1928 }
1816 1929
1817 1930 public static function subscriptionIntervalInDays($interval)
1818 1931 {
1819 - switch ($interval) {
1820 - case 'daily':
1821 - return 1;
1822 - case 'weekly':
1823 - return 7;
1824 - case 'monthly':
1825 - return 30;
1826 - case 'quarterly':
1827 - return 90;
1828 - case 'half_yearly':
1829 - return 182;
1830 - case 'yearly':
1831 - return 365;
1832 - default:
1833 - return apply_filters('fluent_cart/subscription_interval_in_days', 0, [
1834 - 'interval' => $interval,
1835 - ]);
1836 - }
1932 + return PaymentHelper::getIntervalDays($interval);
1837 1933 }
1838 1934
1839 1935 public static function parseTermIdsForFilter($filters): array
1840 1936 {
@@ -1889,12 +1985,12 @@
1889 1985 {
1890 1986 $allChildVariants = Arr::pluck($variants, 'other_info.bundle_child_ids');
1891 1987
1892 1988 $allChildVariants = array_unique(Arr::flatten($allChildVariants));
1893 - $allChildVariants = Arr::except(
1989 + $allChildVariants = array_values(array_diff(
1894 1990 $allChildVariants,
1895 1991 Arr::pluck($variants, 'id')
1896 - );
1992 + ));
1897 1993 $allChildVariants = array_filter($allChildVariants);
1898 1994 $childVariants = ProductVariation::query()
1899 1995 ->whereIn('id', $allChildVariants)
1900 1996 ->with('product:ID,post_title')