Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Utilities
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 6
380
0.00% covered (danger)
0.00%
0 / 1
 can_site_sync_orders
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_order_attribution_enabled
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
90
 normalize_order_status
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 datetime_to_object
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 format_utc_offset
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_site_datetimezone
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * TEMPORARY: interim port for WOOA7S-1550 — remove when the shared sync-modules composer package lands.
4 *
5 * Minimal slice of woocommerce-analytics' HelperTraits\Utilities trait: only the
6 * helpers actually referenced by the ported sync module and its registration.
7 *
8 * @package automattic/jetpack-premium-analytics
9 */
10
11namespace Automattic\Jetpack\PremiumAnalytics\Sync;
12
13use Automattic\WooCommerce\Internal\Features\FeaturesController;
14use DateTimeZone;
15use WC_DateTime;
16
17defined( 'ABSPATH' ) || exit;
18
19/**
20 * Trait Utilities.
21 *
22 * WooCommerce is a runtime (not composer) dependency, so the WC symbols below are
23 * only ever reached when WooCommerce is active. Callers guard with class/function
24 * existence checks; see {@see Configuration::register()}.
25 */
26trait Utilities {
27
28    /**
29     * Can site sync orders to WPCOM infrastructure.
30     *
31     * @return boolean
32     */
33    protected function can_site_sync_orders(): bool {
34        return $this->is_order_attribution_enabled();
35    }
36
37    /**
38     * Check if the order attribution feature is enabled.
39     *
40     * @return bool
41     */
42    protected function is_order_attribution_enabled(): bool {
43
44        if ( ! class_exists( FeaturesController::class ) || ! function_exists( 'wc_get_container' ) ) {
45            return false;
46        }
47
48        try {
49            $feature_controller = wc_get_container()->get( FeaturesController::class );
50            '@phan-var FeaturesController $feature_controller';
51            $is_enabled = $feature_controller->feature_is_enabled( 'order_attribution' );
52
53            /*
54             * When the feature settings form is submitted, feature_is_enabled won't return false right away
55             * We need to check what value was actually posted. Only checked options are posted.
56             * So if it's a POST request and $_POST[ 'woocommerce_custom_orders_table_enabled' ] does not exist
57             * we optimistically assume this is now false.
58             */
59            // phpcs:disable WordPress.Security.NonceVerification.Recommended
60            if ( isset( $_GET['section'] ) && 'features' === $_GET['section'] ) {
61                // phpcs:disable WordPress.Security.NonceVerification.Missing
62                if ( isset( $_POST['woocommerce_feature_order_attribution_enabled'] ) ) {
63                    $posted_order_attribution = wc_clean( sanitize_text_field( wp_unslash( $_POST['woocommerce_feature_order_attribution_enabled'] ) ) );
64                    $is_enabled               = wc_string_to_bool( $posted_order_attribution );
65                } elseif ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
66                    $is_enabled = false;
67                }
68                // phpcs:enable WordPress.Security.NonceVerification.Missing
69            }
70            // phpcs:enable WordPress.Security.NonceVerification.Recommended
71
72            return $is_enabled;
73        } catch ( \Exception $e ) {
74            return false;
75        }
76    }
77
78    /**
79     * Maps order status provided by the user to the one used in the database.
80     *
81     * @param string $status Order status.
82     * @return string
83     */
84    protected static function normalize_order_status( $status ) {
85        $status                 = str_replace( 'wc-', '', $status );
86        $wc_order_status_keys   = array_keys( wc_get_order_statuses() );
87        $wc_order_status_keys[] = 'wc-checkout-draft'; // Related to Woo bug as `wc-checkout-draft` is missing from `wc_get_order_statuses`.
88
89        return in_array( 'wc-' . $status, $wc_order_status_keys, true ) ? 'wc-' . $status : $status;
90    }
91
92    /**
93     * Convert the WC_DateTime objects to stdClass objects to ensure they are properly encoded.
94     *
95     * @param WC_DateTime|mixed $wc_datetime The datetime object.
96     * @param bool              $utc         Whether to convert to UTC.
97     * @return object|null
98     */
99    protected static function datetime_to_object( $wc_datetime, $utc = false ) {
100        if ( is_string( $wc_datetime ) ) {
101            $wc_datetime = new WC_DateTime( $wc_datetime, self::get_site_datetimezone() );
102        }
103
104        if ( is_a( $wc_datetime, 'WC_DateTime' ) ) {
105            if ( $utc ) {
106                $wc_datetime->setTimezone( new DateTimeZone( 'UTC' ) );
107            } else {
108                $wc_datetime->setTimezone( self::get_site_datetimezone() );
109            }
110            return (object) (array) $wc_datetime;
111        }
112    }
113
114    /**
115     * Convert offset in seconds to ISO 8601 timezone offset format.
116     *
117     * @param int|float $offset_seconds The timezone offset in seconds.
118     * @return string The ISO 8601 timezone offset string (e.g., '+08:00', '-08:30', '+00:00').
119     */
120    protected static function format_utc_offset( $offset_seconds ) {
121        $hours   = intval( abs( $offset_seconds ) / HOUR_IN_SECONDS );
122        $minutes = intval( ( abs( $offset_seconds ) % HOUR_IN_SECONDS ) / MINUTE_IN_SECONDS );
123        $sign    = $offset_seconds >= 0 ? '+' : '-';
124
125        return sprintf( '%s%02d:%02d', $sign, $hours, $minutes );
126    }
127
128    /**
129     * Get DateTimeZone object for WooCommerce timezone.
130     *
131     * @return DateTimeZone The DateTimeZone object for the site timezone.
132     */
133    protected static function get_site_datetimezone() {
134        return new DateTimeZone( self::format_utc_offset( wc_timezone_offset() ) );
135    }
136}