ACF
3 weeks ago
WooCommerce
3 weeks ago
ACF.php
3 weeks ago
BulkyBulkEditProductsWooCommerce.php
3 weeks ago
EDD.php
3 weeks ago
Elementor.php
3 weeks ago
Formello.php
3 weeks ago
Integration.php
3 weeks ago
JetEngine.php
3 weeks ago
LimitLoginAttempts.php
3 weeks ago
MonsterInsights.php
3 weeks ago
RankMath.php
3 weeks ago
SchemaPro.php
3 weeks ago
UltimateMember.php
3 weeks ago
WooCommerce.php
3 weeks ago
WooCommerce.php
866 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Makes WooCommerce compatible with WP-Parsidate plugin |
| 4 | * |
| 5 | * @package WP-Parsidate |
| 6 | * @subpackage Plugins/WooCommerce |
| 7 | */ |
| 8 | |
| 9 | namespace WPParsidate\App\Integration; |
| 10 | |
| 11 | defined( 'ABSPATH' ) || exit; |
| 12 | |
| 13 | use Automattic\WooCommerce\Utilities\FeaturesUtil; |
| 14 | use WPParsidate\Addons\Addon; |
| 15 | use WPParsidate\App\Integration\WooCommerce\{WcGateways, WooCommerceCitySelect}; |
| 16 | use WPParsidate\Helper\{Assets, Date, Number}; |
| 17 | use WPParsidate\Settings\Settings; |
| 18 | |
| 19 | class WooCommerce extends Addon { |
| 20 | public string $addonID = 'woocommerce'; |
| 21 | |
| 22 | public string $currentTab = 'woocommerce'; |
| 23 | |
| 24 | public function initM1Action(): void { |
| 25 | add_filter( 'wp_parsidate_' . $this->addonID . '_settings', [ $this, 'addTabSettings' ] ); |
| 26 | add_filter( 'wp_parsidate_' . $this->addonID . '_tab_display_notice', '__return_false' ); |
| 27 | add_filter( 'wp_parsidate_' . $this->addonID . '_tab_content_display_notice', '__return_true' ); |
| 28 | |
| 29 | add_action( 'before_woocommerce_init', [ $this, 'beforeWooCommerceInit' ] ); |
| 30 | |
| 31 | WcGateways::getInstance(); |
| 32 | } |
| 33 | |
| 34 | public function initAction(): void { |
| 35 | add_filter( 'woocommerce_reports_get_order_report_query', [ $this, 'fixOrderReportQueryDate' ] ); |
| 36 | |
| 37 | if ( get_locale() === 'fa_IR' ) { |
| 38 | if ( $this->getSetting( 'fix_prices', false ) ) { |
| 39 | add_filter( 'wc_price', [ $this, 'fixNumbersToPersian' ] ); |
| 40 | add_filter( 'woocommerce_get_price_html', [ $this, 'fixNumbersToPersian' ] ); |
| 41 | add_filter( 'woocommerce_cart_item_price', [ $this, 'fixNumbersToPersian' ] ); |
| 42 | add_filter( 'woocommerce_cart_item_subtotal', [ $this, 'fixNumbersToPersian' ] ); |
| 43 | add_filter( 'woocommerce_cart_subtotal', [ $this, 'fixNumbersToPersian' ] ); |
| 44 | add_filter( 'woocommerce_cart_totals_coupon_html', [ $this, 'fixNumbersToPersian' ] ); |
| 45 | add_filter( 'woocommerce_cart_shipping_method_full_label', [ $this, 'fixNumbersToPersian' ] ); |
| 46 | add_filter( 'woocommerce_cart_total', [ $this, 'fixNumbersToPersian' ] ); |
| 47 | } |
| 48 | |
| 49 | if ( Settings::get( 'persian_date', false ) ) { |
| 50 | add_action( 'wp_head', [ $this, 'fixDateTimeDirection' ] ); |
| 51 | add_filter( 'woocommerce_email_styles', [ $this, 'fixEmailTime' ], 9999, 2 ); |
| 52 | |
| 53 | // Jalali datepicker |
| 54 | add_action( 'admin_enqueue_scripts', [ $this, 'adminEnqueueScripts' ] ); |
| 55 | |
| 56 | // Convert order_date using js |
| 57 | add_action( 'woocommerce_process_shop_order_meta', [ $this, 'changeOrderDateOnSave' ], 1000 ); |
| 58 | add_filter( 'woocommerce_process_product_meta', [ $this, 'validateNonVariableProductDates' ], 1000 ); |
| 59 | add_action( 'woocommerce_ajax_save_product_variations', [ $this, 'validateVariableProductDates' ], 1000 ); |
| 60 | add_action( 'woocommerce_process_shop_coupon_meta', [ $this, 'validateCouponsDate' ], 1000 ); |
| 61 | add_filter( 'get_post_metadata', [ $this, 'changeOrderDateAndCouponExpiresMeta' ], 10, 4 ); |
| 62 | |
| 63 | add_filter( 'manage_edit-shop_coupon_columns', [ $this, 'removeCouponExpiryDateColumn' ] ); |
| 64 | add_action( 'manage_shop_coupon_posts_custom_column', [ $this, 'printCouponExpiryDateColumn' ], 10, 2 ); |
| 65 | |
| 66 | add_action( 'admin_footer', [ $this, 'fixShowCreatedOrderDate' ] ); |
| 67 | add_action( 'admin_init', [ $this, 'changeReportDates' ], 1000 ); |
| 68 | } |
| 69 | |
| 70 | add_filter( 'woocommerce_checkout_process', [ $this, 'acceptPersianNumbersInCheckout' ], 20 ); |
| 71 | add_filter( 'woocommerce_checkout_posted_data', [ $this, 'convertNonPersianValuesInCheckout' ] ); |
| 72 | |
| 73 | add_filter( 'woocommerce_format_postcode', [ $this, 'acceptPersianNumbersInPostCode' ], 9999, 2 ); |
| 74 | // add_filter( 'woocommerce_order_get_shipping_postcode', [ $this, 'fixPersianNumbersInPostCode' ], 9999, 2 ); |
| 75 | |
| 76 | // WC_Order class, get_address_prop method, Filter: 'woocommerce_order_get_[billing|shipping]_[prop]' |
| 77 | add_filter( 'woocommerce_order_get_shipping_phone', [ $this, 'fixPersianNumbersInPhone' ], 9999, 2 ); |
| 78 | |
| 79 | if ( $this->getSetting( 'validate_postcode', false ) ) { |
| 80 | add_filter( 'woocommerce_validate_postcode', [ $this, 'validatePostcode' ], 10, 3 ); |
| 81 | } |
| 82 | |
| 83 | if ( $this->getSetting( 'validate_phone', false ) ) { |
| 84 | add_action( 'woocommerce_after_checkout_validation', [ $this, 'validatePhoneNumber' ], 10, 2 ); |
| 85 | add_filter( 'woocommerce_validate_phone', [ $this, 'validatePhone' ], 10, 3 ); |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * Init Before WooCommerce Loaded |
| 92 | */ |
| 93 | public function beforeWooCommerceInit(): void { |
| 94 | // Include City Translate |
| 95 | if ( $this->getSetting( 'dropdown_cities', false ) ) { |
| 96 | new WooCommerceCitySelect(); |
| 97 | } |
| 98 | |
| 99 | if ( class_exists( FeaturesUtil::class ) ) { |
| 100 | FeaturesUtil::declare_compatibility( 'custom_order_tables', WP_PARSI_ROOT, true ); |
| 101 | FeaturesUtil::declare_compatibility( 'product_instance_caching', WP_PARSI_ROOT, true ); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * @param $data |
| 107 | * @param \WP_Error $errors WP Error |
| 108 | * |
| 109 | * @return void |
| 110 | */ |
| 111 | public function validatePhoneNumber( $data, $errors ): void { |
| 112 | // This pattern ensures the phone number follows the specified structure for both mobile and landline numbers |
| 113 | if ( ! preg_match( '/^(0|0098|\+98)?(9\d{9}|[1-8]\d{9,10})$/', Number::toEnglish( wc_get_post_data_by_key( 'billing_phone' ) ) ) ) { |
| 114 | $errors->add( 'invalid_phone', __( '<strong>Phone number</strong> is invalid.', 'wp-parsidate' ) ); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * Validate phone number |
| 120 | * |
| 121 | * @param bool $valid Whether the phone number passed the default validation. |
| 122 | * @param string $phone The phone number being validated. |
| 123 | * @param string|null $country The country code the phone is being validated for, or null if unknown. |
| 124 | */ |
| 125 | public function validatePhone( $valid, $phone, $country ): bool { |
| 126 | if ( $valid ) { |
| 127 | return $valid; |
| 128 | } |
| 129 | |
| 130 | $phone = Number::toEnglish( $phone ); |
| 131 | |
| 132 | return (bool) preg_match( '/^(0|0098|\+98)?(9\d{9}|[1-8]\d{9,10})$/', $phone ); |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Validate Iranian customer postal code |
| 137 | * |
| 138 | * @param $valid |
| 139 | * @param $postcode |
| 140 | * @param $country |
| 141 | * |
| 142 | * @return bool |
| 143 | */ |
| 144 | public function validatePostcode( $valid, $postcode, $country ): bool { |
| 145 | if ( 'IR' !== $country ) { |
| 146 | return $valid; |
| 147 | } |
| 148 | |
| 149 | return \WPParsidate\Helper\WooCommerce::isPostalCode( $postcode, |
| 150 | apply_filters( 'wp_parsidate_validate_postal_code_checksum', false ) ); |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Convert Non-Persian Values in checkout to Persian |
| 155 | * |
| 156 | * @method convertNonPersianValuesInCheckout |
| 157 | * @param array $data |
| 158 | * |
| 159 | * @return array modified $data |
| 160 | * @version 1.0.0 |
| 161 | * @since 4.0.1 |
| 162 | */ |
| 163 | public function convertNonPersianValuesInCheckout( $data ): array { |
| 164 | $persian_fields = array( |
| 165 | 'billing_postcode', |
| 166 | 'billing_city', |
| 167 | 'billing_address_1', |
| 168 | 'billing_address', |
| 169 | 'billing_address_2', |
| 170 | 'billing_state', |
| 171 | 'shipping_postcode', |
| 172 | 'shipping_city', |
| 173 | 'shipping_address_1', |
| 174 | 'shipping_address', |
| 175 | 'shipping_address_2', |
| 176 | 'shipping_state', |
| 177 | 'billing_first_name', |
| 178 | 'billing_last_name', |
| 179 | 'billing_company', |
| 180 | 'billing_email', |
| 181 | 'shipping_first_name', |
| 182 | 'shipping_last_name', |
| 183 | 'shipping_company', |
| 184 | ); |
| 185 | |
| 186 | /** |
| 187 | * here we pass those fields we want to convert from arabic to persian |
| 188 | * other developers can hook into this filter and add their fields too |
| 189 | * |
| 190 | * @var array $persian_fields |
| 191 | */ |
| 192 | $supported_persian_fields = apply_filters( "wpp_woocommerce_checkout_persian_fields", $persian_fields ); |
| 193 | |
| 194 | foreach ( $supported_persian_fields as $field ) { |
| 195 | if ( isset( $data[ $field ] ) ) { |
| 196 | $data[ $field ] = self::fixPersianCharacters( $data[ $field ] ); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | return apply_filters( "wpp_woocommerce_checkout_modified_persian_fields", $data ); |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * replace Arabic characters with equivalent character in Persian |
| 205 | * |
| 206 | * @method fixPersianCharacters |
| 207 | * @param string $string |
| 208 | * |
| 209 | * @return string filtered $string |
| 210 | * @version 1.0.0 |
| 211 | * @since 4.0.1 |
| 212 | */ |
| 213 | public static function fixPersianCharacters( $string ): string { |
| 214 | $characters = array( |
| 215 | 'ك' => 'ک', |
| 216 | 'دِ' => 'د', |
| 217 | 'بِ' => 'ب', |
| 218 | 'زِ' => 'ز', |
| 219 | 'ذِ' => 'ذ', |
| 220 | 'شِ' => 'ش', |
| 221 | 'سِ' => 'س', |
| 222 | 'ى' => 'ی', |
| 223 | 'ي' => 'ی', |
| 224 | '١' => '۱', |
| 225 | '٢' => '۲', |
| 226 | '٣' => '۳', |
| 227 | '٤' => '۴', |
| 228 | '٥' => '۵', |
| 229 | '٦' => '۶', |
| 230 | '٧' => '۷', |
| 231 | '٨' => '۸', |
| 232 | '٩' => '۹', |
| 233 | '٠' => '۰', |
| 234 | ); |
| 235 | |
| 236 | $characters = apply_filters( "wpp_arabic_persian_characters_list", $characters ); |
| 237 | |
| 238 | return str_replace( array_keys( $characters ), array_values( $characters ), $string ); |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * Fix persian postal code & phone numbers in WooCommerce checkout |
| 243 | * |
| 244 | * @since 4.1.0 |
| 245 | */ |
| 246 | public function acceptPersianNumbersInCheckout(): void { |
| 247 | if ( $this->getSetting( 'fix_persian_postcode', false ) ) { |
| 248 | if ( isset( $_POST['billing_postcode'] ) ) { |
| 249 | $_POST['billing_postcode'] = Number::toEnglish( wc_get_post_data_by_key( 'billing_postcode' ) ); |
| 250 | } |
| 251 | |
| 252 | if ( isset( $_POST['shipping_postcode'] ) ) { |
| 253 | $_POST['shipping_postcode'] = Number::toEnglish( wc_get_post_data_by_key( 'shipping_postcode' ) ); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | if ( $this->getSetting( 'fix_persian_phone', false ) ) { |
| 258 | if ( isset( $_POST['billing_phone'] ) ) { |
| 259 | $_POST['billing_phone'] = Number::toEnglish( wc_get_post_data_by_key( 'billing_phone' ) ); |
| 260 | } |
| 261 | |
| 262 | if ( isset( $_POST['shipping_phone'] ) ) { |
| 263 | $_POST['shipping_phone'] = Number::toEnglish( wc_get_post_data_by_key( 'shipping_phone' ) ); |
| 264 | } |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Fix persian postal code in WooCommerce checkout |
| 270 | * |
| 271 | * @since 6.0 |
| 272 | */ |
| 273 | public function acceptPersianNumbersInPostCode( $postcode, $country ) { |
| 274 | if ( 'IR' === $country && $this->getSetting( 'fix_persian_postcode', false ) ) { |
| 275 | $postcode = Number::toEnglish( $postcode ); |
| 276 | } |
| 277 | |
| 278 | return $postcode; |
| 279 | } |
| 280 | |
| 281 | /** |
| 282 | * Fix non-persian digits in checkout phone field |
| 283 | * |
| 284 | * @param string $phone The address property value. |
| 285 | * @param \WC_Order $order The order object being read. |
| 286 | * |
| 287 | * @since 6.0 |
| 288 | */ |
| 289 | public function fixPersianNumbersInPhone( $phone, $order ): string { |
| 290 | if ( $order->get_shipping_country() === 'IR' && $this->getSetting( 'fix_persian_phone', false ) ) { |
| 291 | $phone = Number::toEnglish( $phone ); |
| 292 | } |
| 293 | |
| 294 | return $phone; |
| 295 | } |
| 296 | |
| 297 | |
| 298 | /** |
| 299 | * Changes gregorian dates to Jalali date on wc report screen |
| 300 | * |
| 301 | * @since 4.0.0 |
| 302 | */ |
| 303 | public function changeReportDates(): void { |
| 304 | $page = sanitize_text_field( wp_unslash( $_GET['page'] ?? '' ) ); |
| 305 | $startDate = sanitize_text_field( wp_unslash( $_GET['start_date'] ?? '' ) ); |
| 306 | $endDate = sanitize_text_field( wp_unslash( $_GET['end_date'] ?? '' ) ); |
| 307 | |
| 308 | if ( ! empty( $page ) && 'wc-reports' === $page ) { |
| 309 | if ( ! empty( $startDate ) ) { |
| 310 | $_GET['start_date'] = gregdate( 'Y-m-d', Number::toEnglish( wc_clean( $startDate ) ) ); |
| 311 | } |
| 312 | |
| 313 | if ( ! empty( $endDate ) ) { |
| 314 | $_GET['end_date'] = gregdate( 'Y-m-d', Number::toEnglish( wc_clean( $endDate ) ) ); |
| 315 | } |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | /** |
| 320 | * Changes order_date field in "Edit order" screen using JS |
| 321 | * |
| 322 | * @since 4.0.0 |
| 323 | */ |
| 324 | public function fixShowCreatedOrderDate(): void { |
| 325 | $current_screen = $this->getCurrentScreen(); |
| 326 | |
| 327 | if ( 'edit_order' === $current_screen ) { |
| 328 | if ( ! \WPParsidate\Helper\WooCommerce::hposEnabled() ) { |
| 329 | global $post; |
| 330 | |
| 331 | if ( ! $post ) { |
| 332 | $jalali_date = parsidate( 'Y-m-d', date( 'Y-m-d' ), 'eng' ); |
| 333 | } else { |
| 334 | $jalali_date = parsidate( 'Y-m-d', date( 'Y-m-d', strtotime( $post->post_date ) ), 'eng' ); |
| 335 | } |
| 336 | } else { |
| 337 | global $theorder; |
| 338 | |
| 339 | if ( ! $theorder ) { |
| 340 | $jalali_date = parsidate( 'Y-m-d', date( 'Y-m-d' ), 'eng' ); |
| 341 | } else { |
| 342 | $jalali_date = parsidate( 'Y-m-d', ! is_null( $theorder->get_date_created() ) ? $theorder->get_date_created()->getOffsetTimestamp() : '', 'eng' ); |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | wp_add_inline_script( 'wpp_jalali_datepicker', 'jQuery("input[name=order_date]").val("' . $jalali_date . '")' ); |
| 347 | |
| 348 | } elseif ( 'legacy_report' === $current_screen ) { |
| 349 | $startDate = sanitize_text_field( wp_unslash( $_GET['start_date'] ?? '' ) ); |
| 350 | $endDate = sanitize_text_field( wp_unslash( $_GET['end_date'] ?? '' ) ); |
| 351 | |
| 352 | $jalali_start_date = ! empty( $startDate ) ? parsidate( 'Y-m-d', |
| 353 | date( 'Y-m-d', strtotime( $startDate ) ), 'eng' ) : ''; |
| 354 | $jalali_end_date = ! empty( $endDate ) ? parsidate( 'Y-m-d', |
| 355 | date( 'Y-m-d', strtotime( $endDate ) ), 'eng' ) : ''; |
| 356 | |
| 357 | wp_add_inline_script( 'wpp_jalali_datepicker', 'jQuery("input[name=start_date]").val("' . $jalali_start_date . '");jQuery("input[name=end_date]").val("' . $jalali_end_date . '");' ); |
| 358 | |
| 359 | } elseif ( 'product' === $current_screen ) { |
| 360 | global $post; |
| 361 | |
| 362 | if ( ! $post ) { |
| 363 | return; |
| 364 | } |
| 365 | |
| 366 | $product = wc_get_product( $post->ID ); |
| 367 | |
| 368 | if ( ! $product ) { |
| 369 | return; |
| 370 | } |
| 371 | |
| 372 | if ( ! $product->is_type( 'variable' ) ) { |
| 373 | $sale_price_dates_from_timestamp = $product->get_date_on_sale_from( 'edit' ) ? $product->get_date_on_sale_from( 'edit' )->getOffsetTimestamp() : false; |
| 374 | $sale_price_dates_to_timestamp = $product->get_date_on_sale_to( 'edit' ) ? $product->get_date_on_sale_to( 'edit' )->getOffsetTimestamp() : false; |
| 375 | |
| 376 | $sale_price_dates_from = $sale_price_dates_from_timestamp ? Number::toEnglish( date_i18n( 'Y-m-d', $sale_price_dates_from_timestamp ) ) : ''; |
| 377 | $sale_price_dates_to = $sale_price_dates_to_timestamp ? Number::toEnglish( date_i18n( 'Y-m-d', $sale_price_dates_to_timestamp ) ) : ''; |
| 378 | |
| 379 | wp_add_inline_script( 'wpp_jalali_datepicker', 'jQuery("#_sale_price_dates_from").val("' . $sale_price_dates_from . '");jQuery("#_sale_price_dates_to").val("' . $sale_price_dates_to . '");' ); |
| 380 | |
| 381 | } else { |
| 382 | $dates = array(); |
| 383 | $loop = 0; |
| 384 | $available_variations = $product->get_available_variations(); |
| 385 | |
| 386 | foreach ( $available_variations as $variation ) { |
| 387 | $variation_id = $variation['variation_id']; |
| 388 | $variation_obj = new \WC_Product_Variation( $variation_id ); |
| 389 | |
| 390 | $sale_price_dates_from_timestamp = $variation_obj->get_date_on_sale_from( 'edit' ) ? $variation_obj->get_date_on_sale_from( 'edit' )->getOffsetTimestamp() : false; |
| 391 | $sale_price_dates_to_timestamp = $variation_obj->get_date_on_sale_to( 'edit' ) ? $variation_obj->get_date_on_sale_to( 'edit' )->getOffsetTimestamp() : false; |
| 392 | |
| 393 | $sale_price_dates_from = $sale_price_dates_from_timestamp ? Number::toEnglish( date_i18n( 'Y-m-d', $sale_price_dates_from_timestamp ) ) : ''; |
| 394 | $sale_price_dates_to = $sale_price_dates_to_timestamp ? Number::toEnglish( date_i18n( 'Y-m-d', $sale_price_dates_to_timestamp ) ) : ''; |
| 395 | $dates[ $loop ] = array( |
| 396 | 'start' => esc_attr( $sale_price_dates_from ), |
| 397 | 'end' => esc_attr( $sale_price_dates_to ), |
| 398 | ); |
| 399 | |
| 400 | $loop ++; |
| 401 | } |
| 402 | |
| 403 | if ( ! empty( $dates ) ) { |
| 404 | wp_add_inline_script( 'wpp_jalali_datepicker', |
| 405 | 'const wppVariationsDates = ' . wp_json_encode( $dates ) . ' |
| 406 | jQuery("#woocommerce-product-data").on("woocommerce_variations_loaded", function(e) { |
| 407 | wppVariationsDates.forEach((date, index) => { |
| 408 | jQuery(`input[name="variable_sale_price_dates_from[${index}]"]`).val(date.start) |
| 409 | jQuery(`input[name="variable_sale_price_dates_to[${index}]"]`).val(date.end) |
| 410 | }) |
| 411 | })' |
| 412 | ); |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | /** |
| 419 | * Remove default wc expire date column in coupons screen and add our custom column |
| 420 | * |
| 421 | * @param $columns |
| 422 | * |
| 423 | * @return mixed |
| 424 | * @since 4.0.0 |
| 425 | */ |
| 426 | public function removeCouponExpiryDateColumn( $columns ) { |
| 427 | unset( $columns['expiry_date'] ); |
| 428 | |
| 429 | $columns['wpp_expiry_date'] = esc_html__( 'Expiry date', 'wp-parsidate' ); |
| 430 | |
| 431 | return $columns; |
| 432 | } |
| 433 | |
| 434 | /** |
| 435 | * Fill our custom date expires column value |
| 436 | * |
| 437 | * @param $column |
| 438 | * @param $postID |
| 439 | * |
| 440 | * @since 4.0.0 |
| 441 | */ |
| 442 | public function printCouponExpiryDateColumn( $column, $postID ): void { |
| 443 | if ( $column === 'wpp_expiry_date' ) { |
| 444 | $date = get_post_meta( $postID, 'date_expires', true ); |
| 445 | |
| 446 | echo ! empty( $date ) ? esc_html( parsidate( 'Y-m-d', $date ) ) : '-'; |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | /** |
| 451 | * Changes coupon expire date on load coupon data |
| 452 | * We use $wpdb to avoid creating an infinite loop |
| 453 | * |
| 454 | * @param $metadata |
| 455 | * @param $object_id |
| 456 | * @param $meta_key |
| 457 | * @param $single |
| 458 | * |
| 459 | * @return int|mixed|string |
| 460 | * |
| 461 | * @since 4.0.0 |
| 462 | */ |
| 463 | public function changeOrderDateAndCouponExpiresMeta( $metadata, $object_id, $meta_key, $single ) { |
| 464 | global $wpdb; |
| 465 | |
| 466 | $post_type = get_post_type( $object_id ); |
| 467 | $editAction = sanitize_text_field( wp_unslash( $_GET['action'] ?? '' ) ) === 'edit'; |
| 468 | |
| 469 | if ( $editAction && 'shop_coupon' === $post_type && 'date_expires' === $meta_key ) { |
| 470 | $metadata = $wpdb->get_var( |
| 471 | $wpdb->prepare( |
| 472 | " |
| 473 | SELECT meta_value |
| 474 | From $wpdb->postmeta |
| 475 | WHERE post_id = %d |
| 476 | AND meta_key = %s |
| 477 | ", |
| 478 | $object_id, |
| 479 | $meta_key |
| 480 | ) |
| 481 | ); |
| 482 | |
| 483 | if ( ! empty( $metadata ) ) { |
| 484 | return parsidate( 'Y-m-d', $metadata, 'eng' ); |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | return $metadata; |
| 489 | } |
| 490 | |
| 491 | public function validateCouponsDate( $coupon_id ): void { |
| 492 | $expiry_date = Number::toEnglish( wc_get_post_data_by_key( 'expiry_date' ) ); |
| 493 | |
| 494 | if ( empty( $expiry_date ) ) { |
| 495 | return; |
| 496 | } |
| 497 | |
| 498 | $coupon = new \WC_Coupon( $coupon_id ); |
| 499 | $fixed_expiry_date = strtotime( gregdate( 'Y-m-d 23:59:59', $expiry_date ) ); |
| 500 | |
| 501 | $coupon->set_props( array( 'date_expires' => $fixed_expiry_date ) ); |
| 502 | $coupon->save(); |
| 503 | } |
| 504 | |
| 505 | /** |
| 506 | * Converts variations selected Jalali dates to gregorian |
| 507 | * |
| 508 | * @param $product_id |
| 509 | * |
| 510 | * @since 4.0.0 |
| 511 | */ |
| 512 | public function validateVariableProductDates( $product_id ): void { |
| 513 | if ( ! isset( $_POST['variable_post_id'] ) ) { |
| 514 | return; |
| 515 | } |
| 516 | |
| 517 | $max_loop = max( array_keys( wp_unslash( $_POST['variable_post_id'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 518 | $parent = wc_get_product( $product_id ); |
| 519 | $data_store = $parent->get_data_store(); |
| 520 | |
| 521 | $data_store->sort_all_product_variations( $parent->get_id() ); |
| 522 | |
| 523 | for ( $i = 0; $i <= $max_loop; $i ++ ) { |
| 524 | if ( ! isset( $_POST['variable_post_id'][ $i ] ) ) { |
| 525 | continue; |
| 526 | } |
| 527 | |
| 528 | $variation_id = absint( $_POST['variable_post_id'][ $i ] ); |
| 529 | $variation = wc_get_product_object( 'variation', $variation_id ); |
| 530 | $props = array(); |
| 531 | |
| 532 | if ( isset( $_POST['variable_sale_price_dates_from'][ $i ] ) ) { |
| 533 | $date_on_sale_from = Number::toEnglish( sanitize_text_field( wp_unslash( $_POST['variable_sale_price_dates_from'][ $i ] ) ) ); |
| 534 | |
| 535 | if ( ! empty( $date_on_sale_from ) ) { |
| 536 | $props['date_on_sale_from'] = gregdate( 'Y-m-d 00:00:00', $date_on_sale_from ); |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | if ( isset( $_POST['variable_sale_price_dates_to'][ $i ] ) ) { |
| 541 | $date_on_sale_to = Number::toEnglish( sanitize_text_field( wp_unslash( $_POST['variable_sale_price_dates_to'][ $i ] ) ) ); |
| 542 | |
| 543 | if ( ! empty( $date_on_sale_to ) ) { |
| 544 | $props['date_on_sale_to'] = gregdate( 'Y-m-d 23:59:59', $date_on_sale_to ); |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | if ( empty( $props ) ) { |
| 549 | continue; |
| 550 | } |
| 551 | |
| 552 | $variation->set_props( $props ); |
| 553 | $variation->save(); |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | /** |
| 558 | * Convert selected Jalali dates to gregorian on woocommerce save non-variable products |
| 559 | * |
| 560 | * @param $product_id $ |
| 561 | * |
| 562 | * @return void |
| 563 | * @author HamidReza Yazdani |
| 564 | * |
| 565 | * @since 4.0.0 |
| 566 | */ |
| 567 | public function validateNonVariableProductDates( $product_id ) { |
| 568 | $props = array(); |
| 569 | |
| 570 | if ( isset( $_POST['_sale_price_dates_from'] ) ) { |
| 571 | $date_on_sale_from = Number::toEnglish( wc_get_post_data_by_key( '_sale_price_dates_from' ) ); |
| 572 | $salePriceTimesFrom = sanitize_text_field( wp_unslash( $_POST['_sale_price_times_from'] ?? '' ) ); |
| 573 | $time_on_sale_from = ! empty( $salePriceTimesFrom ) && Date::isTimeString( $salePriceTimesFrom ) ? Date::isTimeString( $salePriceTimesFrom ) : '00:00:00'; |
| 574 | |
| 575 | if ( ! empty( $date_on_sale_from ) ) { |
| 576 | $props['date_on_sale_from'] = date( 'Y-m-d ' . $time_on_sale_from, |
| 577 | strtotime( gregdate( 'Y-m-d', $date_on_sale_from ) ) ); |
| 578 | } |
| 579 | } |
| 580 | |
| 581 | if ( isset( $_POST['_sale_price_dates_to'] ) ) { |
| 582 | $date_on_sale_to = Number::toEnglish( wc_get_post_data_by_key( '_sale_price_dates_to' ) ); |
| 583 | $salePriceTimesTo = sanitize_text_field( wp_unslash( $_POST['_sale_price_times_to'] ?? '' ) ); |
| 584 | $time_on_sale_to = ! empty( $salePriceTimesTo ) && Date::isTimeString( $salePriceTimesTo ) ? Date::isTimeString( $salePriceTimesTo ) : '23:59:59'; |
| 585 | |
| 586 | if ( ! empty( $date_on_sale_to ) ) { |
| 587 | $props['date_on_sale_to'] = date( 'Y-m-d ' . $time_on_sale_to, strtotime( gregdate( 'Y-m-d', $date_on_sale_to ) ) ); |
| 588 | } |
| 589 | } |
| 590 | |
| 591 | if ( empty( $props ) ) { |
| 592 | return; |
| 593 | } |
| 594 | |
| 595 | $product = wc_get_product( $product_id ); |
| 596 | |
| 597 | $product->set_props( $props ); |
| 598 | $product->save(); |
| 599 | } |
| 600 | |
| 601 | /** |
| 602 | * Convert order date to gregorian before saved at database |
| 603 | * |
| 604 | * @param $order_id |
| 605 | * |
| 606 | * @throws \WC_Data_Exception |
| 607 | * @since 5.0.2 |
| 608 | */ |
| 609 | public function changeOrderDateOnSave( $order_id ): void { |
| 610 | $order_date = wc_get_post_data_by_key( 'order_date' ); |
| 611 | |
| 612 | if ( empty( $order_date ) ) { |
| 613 | return; |
| 614 | } |
| 615 | |
| 616 | $order = wc_get_order( $order_id ); |
| 617 | |
| 618 | if ( ! $order ) { |
| 619 | return; |
| 620 | } |
| 621 | |
| 622 | $hour = str_pad( (int) wc_get_post_data_by_key( 'order_date_hour' ), 2, '0', STR_PAD_LEFT ); |
| 623 | $minute = str_pad( (int) wc_get_post_data_by_key( 'order_date_minute' ), 2, '0', STR_PAD_LEFT ); |
| 624 | $second = str_pad( (int) wc_get_post_data_by_key( 'order_date_second' ), 2, '0', STR_PAD_LEFT ); |
| 625 | $time_stamp = "$order_date $hour:$minute:$second"; |
| 626 | $fixed_date = gregdate( 'Y-m-d H:i:s', $time_stamp ); |
| 627 | $date = gmdate( 'Y-m-d H:i:s', strtotime( $fixed_date ) ); |
| 628 | |
| 629 | $order->set_date_created( $date ); |
| 630 | $order->save(); |
| 631 | } |
| 632 | |
| 633 | /** |
| 634 | * enqueue jalali date picker assets |
| 635 | * |
| 636 | * @since 4.0.0 |
| 637 | */ |
| 638 | public function adminEnqueueScripts(): void { |
| 639 | $screen = get_current_screen(); |
| 640 | $current_screen = is_null( $screen ) ? false : $screen->id; |
| 641 | $pluginVersion = Assets::getVersion(); |
| 642 | $debugName = WP_PARSI_DEBUG_MODE ? '' : '.min'; |
| 643 | |
| 644 | $allowed_screens = array( |
| 645 | 'product', |
| 646 | 'shop_order', |
| 647 | 'woocommerce_page_wc-orders', |
| 648 | 'woocommerce_page_wc-reports', |
| 649 | 'shop_coupon', |
| 650 | ); |
| 651 | |
| 652 | if ( in_array( $current_screen, $allowed_screens, true ) && Settings::get( 'persian_date' ) ) { |
| 653 | wp_enqueue_script( 'wpp_jalali_datepicker', Assets::url( 'js-admin/jalalidatepicker.min.js' ), array( 'jquery-ui-datepicker' ), $pluginVersion, [ 'in_footer' => true ] ); |
| 654 | wp_enqueue_style( 'wpp_jalali_datepicker', Assets::url( "css-admin/jalalidatepicker$debugName.css" ), null, $pluginVersion ); |
| 655 | |
| 656 | do_action( 'wpp_jalali_datepicker_enqueued', 'wc' ); |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | /** |
| 661 | * Fixes jalali order date direction in WooCommerce emails (Issue: https://github.com/wordpress-parsi/wp-parsidate/issues/154) |
| 662 | * |
| 663 | * @param $style |
| 664 | * @param $email |
| 665 | * |
| 666 | * @return string |
| 667 | * @since 5.0.0 |
| 668 | */ |
| 669 | public function fixEmailTime( $style, $email ): string { |
| 670 | return $style . 'time{unicode-bidi:embed!important}'; |
| 671 | } |
| 672 | |
| 673 | /** |
| 674 | * Fixes jalali order date direction in WooCommerce my-account endpoints (Issue: https://github.com/wordpress-parsi/wp-parsidate/issues/154) |
| 675 | * |
| 676 | * @return void |
| 677 | * @since 5.0.0 |
| 678 | */ |
| 679 | public function fixDateTimeDirection(): void { |
| 680 | if ( is_woocommerce() || is_wc_endpoint_url() || is_cart() || is_checkout() ) { |
| 681 | echo '<style>mark.order-date,time{unicode-bidi:embed!important}</style>'; |
| 682 | } |
| 683 | } |
| 684 | |
| 685 | public function fixNumbersToPersian( $content ): string { |
| 686 | return Number::fixNumber( $content ); |
| 687 | } |
| 688 | |
| 689 | /** |
| 690 | * @param $report_data |
| 691 | * |
| 692 | * @return mixed |
| 693 | */ |
| 694 | public function fixOrderReportQueryDate( $report_data ) { |
| 695 | $report_data['where'] = preg_replace_callback( "/posts.post_date\s.=?\s'([^']+)'/i", |
| 696 | [ __CLASS__, 'fixOrderReportQueryDateCallback' ], $report_data['where'] ); |
| 697 | |
| 698 | return $report_data; |
| 699 | } |
| 700 | |
| 701 | /** |
| 702 | * @param $date |
| 703 | * |
| 704 | * @return array|mixed|string|string[] |
| 705 | */ |
| 706 | public function fixOrderReportQueryDateCallback( $date ) { |
| 707 | $startDate = sanitize_text_field( wp_unslash( $_GET['start_date'] ?? '' ) ); |
| 708 | $endDate = sanitize_text_field( wp_unslash( $_GET['end_date'] ?? '' ) ); |
| 709 | |
| 710 | if ( empty( $startDate ) || empty( $endDate ) ) { |
| 711 | return $date[0]; |
| 712 | } |
| 713 | |
| 714 | if ( strpos( $date[0], '=' ) === false ) { |
| 715 | if ( (int) $endDate > 1900 ) { |
| 716 | return $date[0]; |
| 717 | } |
| 718 | |
| 719 | $dt = gregdate( 'Y-m-d', $endDate ); |
| 720 | $dt = date( 'Y-m-d', strtotime( "$dt +1 day" ) ); |
| 721 | } else { |
| 722 | if ( (int) $startDate > 1900 ) { |
| 723 | return $date[0]; |
| 724 | } |
| 725 | |
| 726 | $dt = gregdate( 'Y-m-d', $startDate ); |
| 727 | } |
| 728 | |
| 729 | return substr_replace( $date[0], $dt, - 20, 10 ); |
| 730 | } |
| 731 | |
| 732 | /** |
| 733 | * Check the current screen is the WooCommerce order edit page |
| 734 | * |
| 735 | * @return string |
| 736 | */ |
| 737 | private function getCurrentScreen(): ?string { |
| 738 | if ( 'woocommerce_before_order_object_save' === current_action() ) { |
| 739 | global $pagenow; |
| 740 | |
| 741 | if ( \WPParsidate\Helper\WooCommerce::hposEnabled() ) { |
| 742 | $postID = (int) sanitize_text_field( wp_unslash( $_POST['post_ID'] ?? 0 ) ); |
| 743 | |
| 744 | return $postID && 'shop_order' === get_post_type( $postID ) ? 'edit_order' : ''; |
| 745 | } |
| 746 | |
| 747 | return is_admin() && 'post.php' === $pagenow && isset( $_POST['post_type'] ) && 'shop_order' === $_POST['post_type'] && ! empty( $_POST['order_date'] ) ? 'edit_order' : ''; |
| 748 | } |
| 749 | |
| 750 | if ( 'admin_footer' === current_action() ) { |
| 751 | $screen = get_current_screen(); |
| 752 | $current_screen = is_null( $screen ) ? '' : $screen->id; |
| 753 | |
| 754 | if ( in_array( $current_screen, array( 'shop_order', 'woocommerce_page_wc-orders' ) ) ) { |
| 755 | return 'edit_order'; |
| 756 | } |
| 757 | |
| 758 | if ( 'woocommerce_page_wc-reports' === $current_screen ) { |
| 759 | return 'legacy_report'; |
| 760 | } |
| 761 | |
| 762 | return $current_screen; |
| 763 | } |
| 764 | |
| 765 | return null; |
| 766 | } |
| 767 | |
| 768 | public function addTabSettings(): array { |
| 769 | $settings = array( |
| 770 | 'title' => esc_html__( 'WooCommerce', 'wp-parsidate' ), |
| 771 | 'desc' => esc_html__( 'ParsiDate integration for WooCommerce', 'wp-parsidate' ), |
| 772 | 'settings_key' => $this->addonID, |
| 773 | 'settings' => [ |
| 774 | 'woo_product_start_grid' => array( |
| 775 | 'id' => 'woo_product_start_grid', |
| 776 | 'title' => esc_html__( 'Products', 'wp-parsidate' ), |
| 777 | 'type' => 'startGrid', |
| 778 | ), |
| 779 | 'fix_prices' => array( |
| 780 | 'id' => 'fix_prices', |
| 781 | 'title' => esc_html__( 'Fix prices', 'wp-parsidate' ), |
| 782 | 'type' => 'toggle', |
| 783 | 'default' => false, |
| 784 | 'sanitize' => 'bool' |
| 785 | ), |
| 786 | 'woo_product_end_grid' => array( |
| 787 | 'type' => 'endGrid', |
| 788 | ), |
| 789 | 'woo_checkout_start_grid' => array( |
| 790 | 'id' => 'woo_checkout_start_grid', |
| 791 | 'title' => esc_html__( 'Checkout page', 'wp-parsidate' ), |
| 792 | 'type' => 'startGrid', |
| 793 | ), |
| 794 | 'fix_persian_postcode' => array( |
| 795 | 'id' => 'fix_persian_postcode', |
| 796 | 'title' => esc_html__( 'Fix persian postcode', 'wp-parsidate' ), |
| 797 | 'type' => 'toggle', |
| 798 | 'default' => false, |
| 799 | 'sanitize' => 'bool' |
| 800 | ), |
| 801 | 'fix_persian_phone' => array( |
| 802 | 'id' => 'fix_persian_phone', |
| 803 | 'title' => esc_html__( 'Fix persian phone', 'wp-parsidate' ), |
| 804 | 'type' => 'toggle', |
| 805 | 'default' => false, |
| 806 | 'sanitize' => 'bool' |
| 807 | ), |
| 808 | 'dropdown_cities' => array( |
| 809 | 'id' => 'dropdown_cities', |
| 810 | 'title' => esc_html__( 'Display cities as a drop-down list', 'wp-parsidate' ), |
| 811 | 'type' => 'toggle', |
| 812 | 'default' => false, |
| 813 | 'sanitize' => 'bool' |
| 814 | ), |
| 815 | 'validate_postcode' => array( |
| 816 | 'id' => 'validate_postcode', |
| 817 | 'title' => esc_html__( 'Postcode validation', 'wp-parsidate' ), |
| 818 | 'type' => 'toggle', |
| 819 | 'default' => false, |
| 820 | 'sanitize' => 'bool' |
| 821 | ), |
| 822 | 'validate_phone' => array( |
| 823 | 'id' => 'validate_phone', |
| 824 | 'title' => esc_html__( 'Phone number validation', 'wp-parsidate' ), |
| 825 | 'type' => 'toggle', |
| 826 | 'default' => false, |
| 827 | 'sanitize' => 'bool' |
| 828 | ), |
| 829 | 'woo_checkout_end_grid' => array( |
| 830 | 'type' => 'endGrid', |
| 831 | ) |
| 832 | ] |
| 833 | ); |
| 834 | |
| 835 | return apply_filters( 'wp_parsidate_' . $this->addonID . '_settings_options', $settings ); |
| 836 | } |
| 837 | |
| 838 | public function info(): array { |
| 839 | $icon = '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" x="0" y="0" viewBox="0 0 183.6 47.5"><style>.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#873eff}</style><path d="M77.4 0c-4.3 0-7.1 1.4-9.6 6.1L56.4 27.6V8.5c0-5.7-2.7-8.5-7.7-8.5s-7.1 1.7-9.6 6.5L28.3 27.6V8.7c0-6.1-2.5-8.7-8.6-8.7H7.3C2.6 0 0 2.2 0 6.2s2.5 6.4 7.1 6.4h5.1v24.1c0 6.8 4.6 10.8 11.2 10.8s9.6-2.6 12.9-8.7l7.2-13.5v11.4c0 6.7 4.4 10.8 11.1 10.8s9.2-2.3 13-8.7l16.6-28C87.8 4.7 85.3 0 77.3 0zM108.6 0C95 0 84.7 10.1 84.7 23.8s10.4 23.7 23.9 23.7 23.8-10.1 23.9-23.7c0-13.7-10.4-23.8-23.9-23.8m0 32.9c-5.1 0-8.6-3.8-8.6-9.1s3.5-9.2 8.6-9.2 8.6 3.9 8.6 9.2-3.4 9.1-8.6 9.1M159.7 0c-13.5 0-23.9 10.1-23.9 23.8s10.4 23.7 23.9 23.7 23.9-10.1 23.9-23.7S173.2 0 159.7 0m0 32.9c-5.2 0-8.5-3.8-8.5-9.1s3.4-9.2 8.5-9.2 8.6 3.9 8.6 9.2-3.4 9.1-8.6 9.1" class="st0"/></svg>'; |
| 840 | $menuIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 183.6 47.5"><path d="M70.141 3.572c-3.638 0-6.011 1.187-8.125 5.167L52.36 26.945V10.77c0-4.827-2.287-7.198-6.521-7.198-4.237 0-6.014 1.439-8.132 5.504l-9.145 17.869V10.94c0-5.167-2.119-7.368-7.284-7.368H10.776c-3.981 0-6.182 1.864-6.182 5.25 0 3.389 2.116 5.422 6.013 5.422h4.319v20.41c0 5.757 3.895 9.146 9.486 9.146 5.59 0 8.13-2.203 10.924-7.37l6.097-11.431v9.655c0 5.671 3.726 9.146 9.402 9.146 5.674 0 7.79-1.949 11.011-7.37l14.055-23.711c3.051-5.167.933-9.147-5.842-9.147h.082Zm36.908 0c-11.517 0-20.24 8.554-20.24 20.157 0 11.601 8.806 20.071 20.24 20.071s20.157-8.553 20.242-20.071c0-11.603-8.808-20.157-20.242-20.157m0 27.863c-4.319 0-7.283-3.217-7.283-7.706 0-4.49 2.964-7.792 7.283-7.792 4.32 0 7.285 3.302 7.285 7.792 0 4.489-2.879 7.706-7.285 7.706m51.794-27.863c-11.431 0-20.242 8.554-20.242 20.157 0 11.601 8.811 20.071 20.242 20.071 11.435 0 20.241-8.553 20.241-20.071s-8.806-20.157-20.241-20.157m0 27.863c-4.404 0-7.197-3.217-7.197-7.706 0-4.49 2.879-7.792 7.197-7.792 4.319 0 7.284 3.302 7.284 7.792 0 4.489-2.88 7.706-7.284 7.706" style="stroke:#3c3c3c;fill:none;paint-order:fill;stroke-width:7px;fill-rule:evenodd;clip-rule:evenodd"/></svg>'; |
| 841 | |
| 842 | return array( |
| 843 | 'id' => $this->addonID, |
| 844 | 'title' => esc_html__( 'WooCommerce', 'wp-parsidate' ), |
| 845 | 'desc' => esc_html__( 'ParsiDate integration for WooCommerce', 'wp-parsidate' ), |
| 846 | 'force_enable' => true, |
| 847 | 'has_page' => true, |
| 848 | 'icon' => $icon, |
| 849 | 'menu_icon' => $menuIcon, |
| 850 | 'image_link' => 'https://wordpress.org/plugins/woocommerce/', |
| 851 | 'tags' => [ esc_html__( 'WooCommerce', 'wp-parsidate' ) ], |
| 852 | 'cat' => 'integration', |
| 853 | 'settings_key' => $this->addonID, |
| 854 | 'requires_plugins' => [ |
| 855 | 'woocommerce/woocommerce.php' => array( |
| 856 | 'is_wp_plugin' => true, |
| 857 | 'is_free' => true, |
| 858 | 'plugin_link' => 'https://wordpress.org/plugins/woocommerce/', |
| 859 | 'function_check' => '', |
| 860 | 'class_check' => 'WooCommerce', |
| 861 | ) |
| 862 | ] |
| 863 | ); |
| 864 | } |
| 865 | } |
| 866 |