| 1 |
<?php //phpcs:ignore |
| 2 |
namespace PRAD\Includes\Common; |
| 3 |
|
| 4 |
use PRAD\Includes\Compatibility\BaseCurrency; |
| 5 |
use WC_Data_Store; |
| 6 |
|
| 7 |
defined( 'ABSPATH' ) || exit; |
| 8 |
|
| 9 |
/** |
| 10 |
* Functions class. |
| 11 |
*/ |
| 12 |
class Functions { |
| 13 |
|
| 14 |
/** |
| 15 |
* Setup class. |
| 16 |
* |
| 17 |
* @since v.1.0.0 |
| 18 |
*/ |
| 19 |
public function __construct() { |
| 20 |
} |
| 21 |
|
| 22 |
/** |
| 23 |
* Load style path |
| 24 |
* |
| 25 |
* @param string $style_name style name. |
| 26 |
* @return string |
| 27 |
*/ |
| 28 |
public function get_style_path( $style_name ) { |
| 29 |
return PRAD_URL . 'assets/css/' . $style_name . ( is_rtl() ? '-rtl' : '' ) . '.css'; |
| 30 |
} |
| 31 |
|
| 32 |
/** |
| 33 |
* Enqueue style |
| 34 |
* |
| 35 |
* @param string $handle handle. |
| 36 |
* @param string $style_name style name. |
| 37 |
* @param array $deps dependencies. |
| 38 |
* @return void |
| 39 |
*/ |
| 40 |
public function enqueue_style( $handle, $style_name, $deps = array() ) { |
| 41 |
wp_enqueue_style( |
| 42 |
$handle, |
| 43 |
$this->get_style_path( $style_name ), |
| 44 |
$deps, |
| 45 |
PRAD_VER |
| 46 |
); |
| 47 |
} |
| 48 |
|
| 49 |
/** |
| 50 |
* Enqueue style |
| 51 |
* |
| 52 |
* @param string $handle handle. |
| 53 |
* @param string $script_name script name. |
| 54 |
* @param array $args args. |
| 55 |
* @return void |
| 56 |
*/ |
| 57 |
public function enqueue_script( $handle, $script_name, $args = true ) { |
| 58 |
$script_path = PRAD_URL . 'assets/js/' . $script_name . '.js'; |
| 59 |
$assets = $this->get_script_asset( $script_path ); |
| 60 |
wp_enqueue_script( $handle, $script_path, $assets['dependencies'], $assets['version'], $args ); |
| 61 |
} |
| 62 |
|
| 63 |
/** |
| 64 |
* Read a wp-scripts generated asset file for a built script. |
| 65 |
* |
| 66 |
* WP Scripts (DependencyExtractionWebpackPlugin) generates a sibling |
| 67 |
* `{entry}.asset.php` file containing `dependencies` and `version`. |
| 68 |
* |
| 69 |
* @since 1.5.8 |
| 70 |
* |
| 71 |
* @param string $relative_js_path Relative JS path inside the plugin, e.g. 'assets/js/frontend-script.js'. |
| 72 |
* @param array $fallback_deps Dependencies to use if the asset file does not exist. |
| 73 |
* |
| 74 |
* @return array { dependencies: string[], version: string } |
| 75 |
*/ |
| 76 |
public function get_script_asset( $relative_js_path, $fallback_deps = array() ) { |
| 77 |
$relative_js_path = ltrim( (string) $relative_js_path, '/\\' ); |
| 78 |
$asset_path = preg_replace( '/\\.js$/', '.asset.php', $relative_js_path ); |
| 79 |
$asset_file = trailingslashit( PRAD_PATH ) . str_replace( array( '\\', '//' ), '/', $asset_path ); |
| 80 |
|
| 81 |
$asset = null; |
| 82 |
if ( $asset_file && file_exists( $asset_file ) ) { |
| 83 |
$asset = include $asset_file; |
| 84 |
} |
| 85 |
|
| 86 |
if ( is_array( $asset ) && isset( $asset['dependencies'], $asset['version'] ) ) { |
| 87 |
return array( |
| 88 |
'dependencies' => is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(), |
| 89 |
'version' => (string) $asset['version'], |
| 90 |
); |
| 91 |
} |
| 92 |
|
| 93 |
return array( |
| 94 |
'dependencies' => is_array( $fallback_deps ) ? $fallback_deps : array(), |
| 95 |
'version' => defined( 'PRAD_VER' ) ? PRAD_VER : false, |
| 96 |
); |
| 97 |
} |
| 98 |
|
| 99 |
/** |
| 100 |
* Checks if the Product Addons Pro plugin is active and its license is valid. |
| 101 |
* |
| 102 |
* Verifies whether the plugin located at 'product-addons-pro/product-addons-pro.php' |
| 103 |
* is currently active. If active, it then checks the license status stored in the |
| 104 |
* 'edd_prad_license_data' option to ensure it is marked as 'valid'. |
| 105 |
* |
| 106 |
* @return bool True if the plugin is active and the license is valid, false otherwise. |
| 107 |
*/ |
| 108 |
public function is_lc_active() { |
| 109 |
if ( defined( 'PRAD_PRO_VER' ) ) { |
| 110 |
$license_data = get_option( 'edd_prad_license_data', array() ); |
| 111 |
return isset( $license_data['license'] ) && 'valid' === $license_data['license'] ? true : false; |
| 112 |
} |
| 113 |
return false; |
| 114 |
} |
| 115 |
|
| 116 |
/** |
| 117 |
* Checks if the license has expired. |
| 118 |
* |
| 119 |
* This method checks the stored license data in the WordPress options table |
| 120 |
* and determines if the license status is set to 'expired'. It returns `true` |
| 121 |
* if the license is expired, otherwise `false`. |
| 122 |
* |
| 123 |
* @return bool True if the license is expired, otherwise false. |
| 124 |
*/ |
| 125 |
public function is_lc_expired() { |
| 126 |
$license_data = get_option( 'edd_prad_license_data', array() ); |
| 127 |
return isset( $license_data['license'] ) && 'expired' === $license_data['license'] ? true : false; |
| 128 |
} |
| 129 |
|
| 130 |
/** |
| 131 |
* Checks if the license has expired. |
| 132 |
* |
| 133 |
* This method checks the stored license data in the WordPress options table |
| 134 |
* and determines if the license status is set to 'expired'. It returns `true` |
| 135 |
* if the license is expired, otherwise `false`. |
| 136 |
* |
| 137 |
* @return bool True if the license is expired, otherwise false. |
| 138 |
*/ |
| 139 |
public function handle_all_pro_block() { |
| 140 |
if ( defined( 'PRAD_PRO_VER' ) ) { |
| 141 |
$license_data = get_option( 'edd_prad_license_data', array() ); |
| 142 |
return isset( $license_data['license'] ) && ( 'valid' === $license_data['license'] || 'expired' === $license_data['license'] ) ? true : false; |
| 143 |
} |
| 144 |
return false; |
| 145 |
} |
| 146 |
|
| 147 |
/** |
| 148 |
* Checks if the pro feature is available based on plugin activation and license status. |
| 149 |
* |
| 150 |
* Returns true if the Product Addons Pro plugin is active and the license is either valid or expired. |
| 151 |
* |
| 152 |
* @return bool True if pro features are available, false otherwise. |
| 153 |
*/ |
| 154 |
public function is_pro_feature_available() { |
| 155 |
if ( defined( 'PRAD_PRO_VER' ) ) { |
| 156 |
$license_data = get_option( 'edd_prad_license_data', array() ); |
| 157 |
return isset( $license_data['license'] ) && ( 'valid' === $license_data['license'] || 'expired' === $license_data['license'] ) ? true : false; |
| 158 |
} |
| 159 |
return false; |
| 160 |
} |
| 161 |
|
| 162 |
/** |
| 163 |
* Retrieves the assigned product data for a given option. |
| 164 |
* |
| 165 |
* @since 1.0.0 |
| 166 |
* |
| 167 |
* @param int $option_id The ID of the option to retrieve assigned data for. |
| 168 |
* |
| 169 |
* @return object An object containing the assigned data, including 'aType', 'includes', and 'excludes'. |
| 170 |
*/ |
| 171 |
public function get_assigned_product_data( $option_id ) { |
| 172 |
$assigned_data = json_decode( product_addons()->safe_stripslashes( get_post_meta( $option_id, 'prad_base_assigned_data', true ) ), true ); |
| 173 |
if ( empty( $assigned_data ) ) { |
| 174 |
return (object) array( |
| 175 |
'aType' => 'specific_product', |
| 176 |
'includes' => array(), |
| 177 |
'excludes' => array(), |
| 178 |
'excludeCategories' => array(), |
| 179 |
); |
| 180 |
} else { |
| 181 |
if ( isset( $assigned_data['includes'] ) && count( $assigned_data['includes'] ) > 0 ) { |
| 182 |
if ( 'specific_product' === $assigned_data['aType'] ) { |
| 183 |
$includes = $this->get_searched_products( '', false, '', $assigned_data['includes'] ); |
| 184 |
} elseif ( 'specific_category' === $assigned_data['aType'] || 'specific_tag' === $assigned_data['aType'] || 'specific_brand' === $assigned_data['aType'] ) { |
| 185 |
$term_type = 'specific_category' === $assigned_data['aType'] ? 'cat' : ( 'specific_tag' === $assigned_data['aType'] ? 'tag' : 'brand' ); |
| 186 |
$includes = $this->get_searched_categories( |
| 187 |
array( |
| 188 |
'term' => '', |
| 189 |
'limit' => '', |
| 190 |
'includes' => $assigned_data['includes'], |
| 191 |
'trigger_type' => $term_type, |
| 192 |
) |
| 193 |
); |
| 194 |
} else { |
| 195 |
$includes = array(); |
| 196 |
} |
| 197 |
} else { |
| 198 |
$includes = array(); |
| 199 |
} |
| 200 |
|
| 201 |
return (object) array( |
| 202 |
'aType' => $assigned_data['aType'], |
| 203 |
'includes' => $includes, |
| 204 |
'excludes' => isset( $assigned_data['excludes'] ) && count( $assigned_data['excludes'] ) > 0 |
| 205 |
? $this->get_searched_products( '', false, '', $assigned_data['excludes'] ) |
| 206 |
: array(), |
| 207 |
'excludeCategories' => isset( $assigned_data['excludeCategories'] ) && count( $assigned_data['excludeCategories'] ) > 0 |
| 208 |
? $this->get_searched_categories( |
| 209 |
array( |
| 210 |
'term' => '', |
| 211 |
'limit' => '', |
| 212 |
'includes' => $assigned_data['excludeCategories'], |
| 213 |
'trigger_type' => 'cat', |
| 214 |
) |
| 215 |
) |
| 216 |
: array(), |
| 217 |
); |
| 218 |
} |
| 219 |
} |
| 220 |
|
| 221 |
/** |
| 222 |
* Retrieves a list of searched products based on the provided term, including optional product variations. |
| 223 |
* |
| 224 |
* @since 1.0.0 |
| 225 |
* |
| 226 |
* @param string $term The search term. |
| 227 |
* @param bool $include_variations Whether to include product variations in the search. Default is false. |
| 228 |
* @param int $limit The number of products to return. Defaults to all. |
| 229 |
* @param array $include_ids Array of product IDs to include in the search. |
| 230 |
* @param array $tax_filter Optional. Restrict results to products in the given taxonomy terms, |
| 231 |
* e.g. array( 'taxonomy' => 'product_cat', 'term_ids' => array( 1, 2 ) ). |
| 232 |
* |
| 233 |
* @return array An array of product details, including item ID, URL, name, and thumbnail URL. |
| 234 |
*/ |
| 235 |
public function get_searched_products( $term, $include_variations = false, $limit = '', $include_ids = array(), $tax_filter = array() ) { |
| 236 |
$taxonomy = isset( $tax_filter['taxonomy'] ) ? $tax_filter['taxonomy'] : ''; |
| 237 |
$term_ids = isset( $tax_filter['term_ids'] ) && is_array( $tax_filter['term_ids'] ) ? array_map( 'absint', $tax_filter['term_ids'] ) : array(); |
| 238 |
|
| 239 |
if ( $taxonomy && $term_ids ) { |
| 240 |
$ids = $this->get_searched_product_ids_by_taxonomy( $term, $taxonomy, $term_ids, $limit ); |
| 241 |
} else { |
| 242 |
// Load the product data store. |
| 243 |
$data_store = WC_Data_Store::load( 'product' ); |
| 244 |
$exclude_ids = array(); |
| 245 |
$ids = $data_store->search_products( $term, '', (bool) $include_variations, false, $limit, $include_ids, $exclude_ids ); |
| 246 |
} |
| 247 |
|
| 248 |
$products = array(); |
| 249 |
|
| 250 |
foreach ( $ids as $product_id ) { |
| 251 |
$product = wc_get_product( $product_id ); |
| 252 |
|
| 253 |
if ( $product ) { |
| 254 |
$products[] = array( |
| 255 |
'item_id' => $product_id, |
| 256 |
'url' => get_permalink( $product_id ), |
| 257 |
'item_name' => rawurldecode( wp_strip_all_tags( $product->get_name() ) ), |
| 258 |
'thumbnail' => wp_get_attachment_url( $product->get_image_id() ), |
| 259 |
'regular' => $product->get_regular_price(), |
| 260 |
'sale' => $product->get_sale_price(), |
| 261 |
); |
| 262 |
} |
| 263 |
} |
| 264 |
|
| 265 |
return $products; |
| 266 |
} |
| 267 |
|
| 268 |
/** |
| 269 |
* Finds product IDs matching a keyword and belonging to the given taxonomy terms. |
| 270 |
* |
| 271 |
* WC_Data_Store::search_products() has no taxonomy support, so a scoped |
| 272 |
* product-picker search (e.g. excluding products from a chosen category) |
| 273 |
* is run through WP_Query with a tax_query instead. |
| 274 |
* |
| 275 |
* @since 1.6.17 |
| 276 |
* |
| 277 |
* @param string $term The search term. |
| 278 |
* @param string $taxonomy The taxonomy to filter by (product_cat, product_tag, product_brand). |
| 279 |
* @param array $term_ids Term IDs within that taxonomy to restrict results to. |
| 280 |
* @param int $limit The number of products to return. |
| 281 |
* |
| 282 |
* @return array Product IDs. |
| 283 |
*/ |
| 284 |
private function get_searched_product_ids_by_taxonomy( $term, $taxonomy, $term_ids, $limit = '' ) { |
| 285 |
$query_args = array( |
| 286 |
'post_type' => 'product', |
| 287 |
'post_status' => 'publish', |
| 288 |
'fields' => 'ids', |
| 289 |
'posts_per_page' => $limit ? (int) $limit : -1, |
| 290 |
's' => $term, |
| 291 |
'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query |
| 292 |
array( |
| 293 |
'taxonomy' => $taxonomy, |
| 294 |
'field' => 'term_id', |
| 295 |
'terms' => $term_ids, |
| 296 |
), |
| 297 |
), |
| 298 |
); |
| 299 |
|
| 300 |
$query = new \WP_Query( $query_args ); |
| 301 |
|
| 302 |
return $query->posts; |
| 303 |
} |
| 304 |
|
| 305 |
/** |
| 306 |
* Retrieves a list of searched product categories based on the provided term. |
| 307 |
* |
| 308 |
* @since 1.0.0 |
| 309 |
* |
| 310 |
* @param mixed $params The search parameters. |
| 311 |
* |
| 312 |
* @return array An array of category details, including item ID, name, URL, and thumbnail URL. |
| 313 |
*/ |
| 314 |
public function get_searched_categories( $params ) { |
| 315 |
$term = isset( $params['term'] ) ? $params['term'] : ''; |
| 316 |
$limit = isset( $params['limit'] ) ? $params['limit'] : ''; |
| 317 |
$includes = isset( $params['includes'] ) && is_array( $params['includes'] ) ? $params['includes'] : array(); |
| 318 |
$trigger_type = isset( $params['trigger_type'] ) ? $params['trigger_type'] : 'cat'; |
| 319 |
|
| 320 |
$found_categories = array(); |
| 321 |
$args = array( |
| 322 |
'taxonomy' => array( 'product_' . $trigger_type ), |
| 323 |
'orderby' => 'id', |
| 324 |
'number' => $limit, |
| 325 |
'order' => 'ASC', |
| 326 |
'hide_empty' => false, |
| 327 |
'fields' => 'all', |
| 328 |
'name__like' => $term, |
| 329 |
'include' => $includes, |
| 330 |
); |
| 331 |
|
| 332 |
$categories = get_terms( $args ); |
| 333 |
foreach ( $categories as $category ) { |
| 334 |
if ( ! is_wp_error( $category ) ) { |
| 335 |
$found_categories[] = array( |
| 336 |
'item_id' => $category->term_id, |
| 337 |
'item_name' => $category->name, |
| 338 |
'url' => get_term_link( $category ), |
| 339 |
'thumbnail' => get_term_meta( $category->term_id, 'thumbnail_id', true ) |
| 340 |
? wp_get_attachment_url( get_term_meta( $category->term_id, 'thumbnail_id', true ) ) |
| 341 |
: wc_placeholder_img_src(), |
| 342 |
); |
| 343 |
} |
| 344 |
} |
| 345 |
|
| 346 |
return $found_categories; |
| 347 |
} |
| 348 |
|
| 349 |
/** |
| 350 |
* Renders and enqueues inline CSS for a specific addon post. |
| 351 |
* |
| 352 |
* Retrieves the custom CSS stored in the post meta with key 'prad_addons_css' |
| 353 |
* for the given post ID. If CSS is found, it registers a dummy style handle, |
| 354 |
* enqueues it, and adds the CSS inline using `wp_add_inline_style`. |
| 355 |
* |
| 356 |
* @param int $id The post ID from which to retrieve and render the addon CSS. |
| 357 |
* @param mixed $type The context in which to render the CSS (e.g., 'print' for printing). |
| 358 |
*/ |
| 359 |
public function render_addon_css( $id, $type = '' ) { |
| 360 |
$prad_addons_css = get_post_meta( $id, 'prad_addons_css', true ); |
| 361 |
if ( $prad_addons_css ) { |
| 362 |
wp_register_style( 'prad-addons-css-'. $id, false ); // phpcs:ignore |
| 363 |
wp_enqueue_style( 'prad-addons-css-' . $id ); |
| 364 |
wp_add_inline_style( 'prad-addons-css-' . $id, $prad_addons_css ); |
| 365 |
} |
| 366 |
if ( 'print' === $type ) { |
| 367 |
echo '<style id="prad-addons-css-' . esc_attr( $id ) . '-inline">' . esc_html( $prad_addons_css ) . '</style>'; |
| 368 |
} |
| 369 |
} |
| 370 |
|
| 371 |
/** |
| 372 |
* Sanitize Params |
| 373 |
* |
| 374 |
* Recursively sanitizes the given parameters to ensure safe data handling. |
| 375 |
* |
| 376 |
* @param mixed $params The data to be sanitized. Can be an array, boolean, object, or string. |
| 377 |
* |
| 378 |
* @return mixed The sanitized data. |
| 379 |
*/ |
| 380 |
public function sanitize_rest_params( $params ) { |
| 381 |
if ( is_array( $params ) ) { |
| 382 |
return array_map( array( $this, 'sanitize_rest_params' ), $params ); |
| 383 |
} elseif ( is_bool( $params ) ) { |
| 384 |
return rest_sanitize_boolean( $params ); |
| 385 |
} elseif ( is_object( $params ) ) { |
| 386 |
return $params; |
| 387 |
} elseif ( is_string( $params ) ) { |
| 388 |
return $params; |
| 389 |
} else { |
| 390 |
return $params; |
| 391 |
} |
| 392 |
} |
| 393 |
|
| 394 |
/** |
| 395 |
* Returns a structured price object with numeric and formatted HTML price. |
| 396 |
* |
| 397 |
* If a sale price is provided, the returned price is the sale price, and the |
| 398 |
* HTML includes both the regular and sale prices. Otherwise, it returns only |
| 399 |
* the regular price. |
| 400 |
* |
| 401 |
* @since 1.0.3 |
| 402 |
* |
| 403 |
* @param float|string $regular The regular price. |
| 404 |
* @param float|string $sale The sale price. If empty or false, regular price is used. |
| 405 |
* |
| 406 |
* @return array { |
| 407 |
* @type float $price The numeric value of the applicable price. |
| 408 |
* @type string $html The formatted HTML price string. |
| 409 |
* } |
| 410 |
*/ |
| 411 |
public function get_price_object( $regular, $sale ) { |
| 412 |
return array( |
| 413 |
'price' => $sale ? floatval( $sale ) : floatval( $regular ), |
| 414 |
'html' => $sale ? '<span class="pricex"><del>' . wc_price( $regular ) . '</del> <ins>' . wc_price( $sale ) . '</ins></span>' : '<span class="pricex">' . wc_price( $regular ) . '</span>', |
| 415 |
); |
| 416 |
} |
| 417 |
|
| 418 |
/** |
| 419 |
* Converts the given price to the current currency based on active currency switchers. |
| 420 |
* |
| 421 |
* This function checks for various currency switcher plugins (e.g., WowStore Switcher, |
| 422 |
* WooCommerce Currency Switcher, CURCY, Yay_Currency, etc.) and applies the appropriate |
| 423 |
* conversion logic to the provided price. If no currency switcher is active, the original |
| 424 |
* price is returned. |
| 425 |
* |
| 426 |
* @since 1.0.4 |
| 427 |
* |
| 428 |
* @param float $price The original price to be converted. |
| 429 |
* |
| 430 |
* @return float The converted price based on the active currency switcher, or the original price if no switcher is active. |
| 431 |
*/ |
| 432 |
public function get_currency_converted_price( $price ) { |
| 433 |
$price = floatval( $price ); |
| 434 |
|
| 435 |
// WowStore Switcher. |
| 436 |
if ( defined( 'WOPB_VER' ) && defined( 'WOPB_PRO_VER' ) && class_exists( 'WOPB_PRO\Currency_Switcher_Action' ) ) { |
| 437 |
$current_currency_code = wopb_function()->get_setting( 'wopb_current_currency' ); |
| 438 |
$default_currency = wopb_function()->get_setting( 'wopb_default_currency' ); |
| 439 |
$current_currency = \WOPB_PRO\Currency_Switcher_Action::get_currency( $current_currency_code ); |
| 440 |
if ( ! $current_currency ) { |
| 441 |
$current_currency = $default_currency; |
| 442 |
} |
| 443 |
|
| 444 |
if ( $current_currency_code !== $default_currency ) { |
| 445 |
$wopb_current_currency_rate = floatval( ( isset( $current_currency['wopb_currency_rate'] ) && $current_currency['wopb_currency_rate'] > 0 && ! ( '' === $current_currency['wopb_currency_rate'] ) ) ? $current_currency['wopb_currency_rate'] : 1 ); |
| 446 |
$wopb_current_exchange_fee = floatval( ( isset( $current_currency['wopb_currency_exchange_fee'] ) && $current_currency['wopb_currency_exchange_fee'] >= 0 && ! ( '' === $current_currency['wopb_currency_exchange_fee'] ) ) ? $current_currency['wopb_currency_exchange_fee'] : 0 ); |
| 447 |
$total_rate = ( $wopb_current_currency_rate + $wopb_current_exchange_fee ); |
| 448 |
return $price * $total_rate; |
| 449 |
} |
| 450 |
} |
| 451 |
|
| 452 |
// WooCommerce Currency Switcher by WPExperts. |
| 453 |
if ( defined( 'WCCS_VERSION' ) ) { |
| 454 |
$price = apply_filters( 'woocommerce_product_addons_option_price_raw', $price, '' ); //phpcs:ignore |
| 455 |
return $price; |
| 456 |
} |
| 457 |
|
| 458 |
if ( function_exists( 'wmc_get_price' ) ) { |
| 459 |
if ( defined( 'WOOMULTI_CURRENCY_VERSION' ) && class_exists( 'WOOMULTI_CURRENCY_Data' ) ) { |
| 460 |
$curcy = \WOOMULTI_CURRENCY_Data::get_ins(); |
| 461 |
} elseif ( defined( 'WOOMULTI_CURRENCY_F_VERSION' ) && class_exists( 'WOOMULTI_CURRENCY_F_Data' ) ) { |
| 462 |
$curcy = \WOOMULTI_CURRENCY_F_Data::get_ins(); |
| 463 |
} |
| 464 |
|
| 465 |
if ( isset( $curcy ) && $curcy->get_enable() ) { |
| 466 |
$price = wmc_get_price( $price ); |
| 467 |
return $price; |
| 468 |
} |
| 469 |
} |
| 470 |
|
| 471 |
// Yay_Currency Switcher. |
| 472 |
if ( defined( 'YAY_CURRENCY_VERSION' ) ) { |
| 473 |
$price = apply_filters( 'yay_currency_convert_price', $price, '' ); // phpcs:ignore |
| 474 |
return $price; |
| 475 |
} |
| 476 |
|
| 477 |
// FOX - Currency Switcher. |
| 478 |
if ( defined( 'WOOCS_VERSION' ) ) { |
| 479 |
$price = apply_filters( 'woocs_convert_price', $price, '' );// phpcs:ignore |
| 480 |
return $price; |
| 481 |
} |
| 482 |
|
| 483 |
// Currency Switcher for WooCommerce by wpwham. |
| 484 |
if ( function_exists( 'alg_get_current_currency_code' ) && function_exists( 'alg_convert_price' ) ) { |
| 485 |
$default_currency = get_option( 'woocommerce_currency' ); |
| 486 |
$current_currency_code = alg_get_current_currency_code(); |
| 487 |
if ( $current_currency_code !== $default_currency ) { |
| 488 |
$price = alg_convert_price( |
| 489 |
array( |
| 490 |
'price' => $price, |
| 491 |
'currency' => $current_currency_code, |
| 492 |
'currency_from' => $default_currency, |
| 493 |
'format_price' => 'no', |
| 494 |
) |
| 495 |
); |
| 496 |
return $price; |
| 497 |
} |
| 498 |
} |
| 499 |
|
| 500 |
// Yith Currency Switcher. |
| 501 |
if ( function_exists( 'yith_wcmcs_convert_price' ) ) { |
| 502 |
$price = apply_filters( 'yith_wcmcs_convert_price', $price, '' );// phpcs:ignore |
| 503 |
return $price; |
| 504 |
} |
| 505 |
|
| 506 |
// Aelia Currency Switcher. |
| 507 |
if ( class_exists( 'WC_Aelia_CurrencySwitcher' ) ) { |
| 508 |
$base_currency = apply_filters( 'wc_aelia_cs_base_currency', '' );// phpcs:ignore |
| 509 |
$active_currency = get_woocommerce_currency(); |
| 510 |
$price = apply_filters( 'wc_aelia_cs_convert', $price, $base_currency, $active_currency );// phpcs:ignore |
| 511 |
return $price; |
| 512 |
} |
| 513 |
|
| 514 |
if ( class_exists( '\WCPay\MultiCurrency\MultiCurrency' ) ) { |
| 515 |
$multi_currency = null; |
| 516 |
|
| 517 |
if ( class_exists( 'WC_Payments' ) && method_exists( '\WC_Payments', 'get_gateway' ) ) { |
| 518 |
$gateway = \WC_Payments::get_gateway(); |
| 519 |
|
| 520 |
if ( class_exists( '\WCPay\WC_Payments_Currency_Manager' ) ) { |
| 521 |
$currency_manager = new \WCPay\WC_Payments_Currency_Manager( $gateway ); |
| 522 |
|
| 523 |
if ( method_exists( $currency_manager, 'get_multi_currency_instance' ) ) { |
| 524 |
$multi_currency = $currency_manager->get_multi_currency_instance(); |
| 525 |
} |
| 526 |
} |
| 527 |
} |
| 528 |
|
| 529 |
// Convert price if instance exists. |
| 530 |
if ( $multi_currency instanceof \WCPay\MultiCurrency\MultiCurrency && method_exists( $multi_currency, 'get_price' ) ) { |
| 531 |
$price = $multi_currency->get_price( $price, 'product' ); |
| 532 |
return $price; |
| 533 |
} |
| 534 |
} |
| 535 |
|
| 536 |
return $price; |
| 537 |
} |
| 538 |
|
| 539 |
/** |
| 540 |
* Revert the given price to the base price. |
| 541 |
* |
| 542 |
* Price is returned. |
| 543 |
* |
| 544 |
* @since 1.0.4 |
| 545 |
* |
| 546 |
* @param float $price The original price to be reverted. |
| 547 |
* |
| 548 |
* @return float The reverted price. |
| 549 |
*/ |
| 550 |
public function get_currency_reverted_price( $price ) { |
| 551 |
$price = floatval( $price ); |
| 552 |
|
| 553 |
// WowStore Switcher. |
| 554 |
if ( defined( 'WOPB_VER' ) && defined( 'WOPB_PRO_VER' ) && class_exists( 'WOPB_PRO\Currency_Switcher_Action' ) ) { |
| 555 |
$current_currency_code = wopb_function()->get_setting( 'wopb_current_currency' ); |
| 556 |
$default_currency = wopb_function()->get_setting( 'wopb_default_currency' ); |
| 557 |
$current_currency = \WOPB_PRO\Currency_Switcher_Action::get_currency( $current_currency_code ); |
| 558 |
if ( ! $current_currency ) { |
| 559 |
$current_currency = $default_currency; |
| 560 |
} |
| 561 |
|
| 562 |
if ( $current_currency_code !== $default_currency ) { |
| 563 |
$wopb_current_currency_rate = floatval( ( isset( $current_currency['wopb_currency_rate'] ) && $current_currency['wopb_currency_rate'] > 0 && ! ( '' === $current_currency['wopb_currency_rate'] ) ) ? $current_currency['wopb_currency_rate'] : 1 ); |
| 564 |
$wopb_current_exchange_fee = floatval( ( isset( $current_currency['wopb_currency_exchange_fee'] ) && $current_currency['wopb_currency_exchange_fee'] >= 0 && ! ( '' === $current_currency['wopb_currency_exchange_fee'] ) ) ? $current_currency['wopb_currency_exchange_fee'] : 0 ); |
| 565 |
$total_rate = ( $wopb_current_currency_rate + $wopb_current_exchange_fee ); |
| 566 |
return $price / $total_rate; |
| 567 |
} |
| 568 |
} |
| 569 |
|
| 570 |
// WooCommerce Currency Switcher by WPExperts. |
| 571 |
if ( defined( 'WCCS_VERSION' ) ) { |
| 572 |
// Dont have any filter to revert the price. |
| 573 |
$price = $this->manual_currency_reverted_price( $price ); |
| 574 |
return $price; |
| 575 |
} |
| 576 |
|
| 577 |
if ( function_exists( 'wmc_revert_price' ) ) { |
| 578 |
|
| 579 |
if ( defined( 'WOOMULTI_CURRENCY_VERSION' ) && class_exists( 'WOOMULTI_CURRENCY_Data' ) ) { |
| 580 |
$curcy = \WOOMULTI_CURRENCY_Data::get_ins(); |
| 581 |
} elseif ( defined( 'WOOMULTI_CURRENCY_F_VERSION' ) && class_exists( 'WOOMULTI_CURRENCY_F_Data' ) ) { |
| 582 |
$curcy = \WOOMULTI_CURRENCY_F_Data::get_ins(); |
| 583 |
} |
| 584 |
|
| 585 |
if ( isset( $curcy ) && $curcy->get_enable() ) { |
| 586 |
$price = wmc_revert_price( $price ); |
| 587 |
return $price; |
| 588 |
} |
| 589 |
} |
| 590 |
|
| 591 |
// Yay_Currency Switcher. |
| 592 |
if ( defined( 'YAY_CURRENCY_VERSION' ) ) { |
| 593 |
$price = apply_filters( 'yay_currency_revert_price', $price, '' );// phpcs:ignore |
| 594 |
return $price; |
| 595 |
} |
| 596 |
|
| 597 |
// FOX - Currency Switcher. |
| 598 |
if ( defined( 'WOOCS_VERSION' ) ) { |
| 599 |
$price = apply_filters( 'woocs_back_convert_price', $price, '' );// phpcs:ignore |
| 600 |
return $price; |
| 601 |
} |
| 602 |
|
| 603 |
// Currency Switcher for WooCommerce by wpwham. |
| 604 |
if ( function_exists( 'alg_get_current_currency_code' ) && function_exists( 'alg_convert_price' ) ) { |
| 605 |
// Dont have any filter to revert the price. |
| 606 |
$price = $this->manual_currency_reverted_price( $price ); |
| 607 |
return $price; |
| 608 |
} |
| 609 |
|
| 610 |
// Yith Currency Switcher. |
| 611 |
if ( function_exists( 'yith_wcmcs_convert_price' ) ) { |
| 612 |
// Dont have any filter to revert the price. |
| 613 |
$price = $this->manual_currency_reverted_price( $price ); |
| 614 |
return $price; |
| 615 |
} |
| 616 |
|
| 617 |
// Aelia Currency Switcher. |
| 618 |
if ( class_exists( 'WC_Aelia_CurrencySwitcher' ) ) { |
| 619 |
$base_currency = apply_filters( 'wc_aelia_cs_base_currency', '' );// phpcs:ignore |
| 620 |
$active_currency = get_woocommerce_currency(); |
| 621 |
$price = apply_filters( 'wc_aelia_cs_convert', $price, $active_currency, $base_currency );// phpcs:ignore |
| 622 |
return $price; |
| 623 |
} |
| 624 |
|
| 625 |
if ( class_exists( '\WCPay\MultiCurrency\MultiCurrency' ) ) { |
| 626 |
$multi_currency = null; |
| 627 |
$converted_price = $price; |
| 628 |
|
| 629 |
if ( class_exists( 'WC_Payments' ) && method_exists( '\WC_Payments', 'get_gateway' ) ) { |
| 630 |
$gateway = \WC_Payments::get_gateway(); |
| 631 |
|
| 632 |
if ( class_exists( '\WCPay\WC_Payments_Currency_Manager' ) ) { |
| 633 |
$currency_manager = new \WCPay\WC_Payments_Currency_Manager( $gateway ); |
| 634 |
|
| 635 |
if ( method_exists( $currency_manager, 'get_multi_currency_instance' ) ) { |
| 636 |
$multi_currency = $currency_manager->get_multi_currency_instance(); |
| 637 |
} |
| 638 |
} |
| 639 |
} |
| 640 |
|
| 641 |
// Convert price if instance exists. |
| 642 |
if ( $multi_currency instanceof \WCPay\MultiCurrency\MultiCurrency && method_exists( $multi_currency, 'get_price' ) ) { |
| 643 |
$currency = $multi_currency->get_selected_currency(); |
| 644 |
$rate = $currency->get_rate(); |
| 645 |
if ( $currency->get_is_default() || $rate <= 0 ) { |
| 646 |
return $converted_price; |
| 647 |
} |
| 648 |
|
| 649 |
// Reverse conversion. |
| 650 |
$base_price = (float) $converted_price / $rate; |
| 651 |
|
| 652 |
return (float) $base_price; |
| 653 |
|
| 654 |
} |
| 655 |
} |
| 656 |
|
| 657 |
return $price; |
| 658 |
} |
| 659 |
|
| 660 |
/** |
| 661 |
* Retrieves currency conversion data including rate and extra value. |
| 662 |
* |
| 663 |
* This method calculates the converted currency rate by comparing |
| 664 |
* two values from `get_currency_converted_price()`. It returns an array |
| 665 |
* with the active status, rate difference, and extra amount. |
| 666 |
* |
| 667 |
* @since 1.0.4 |
| 668 |
* @return array { |
| 669 |
* @type bool $cr_active Whether currency conversion is active (rate ≠ 1). |
| 670 |
* @type float $cr_rate The currency conversion rate difference. |
| 671 |
* @type float $cr_extra The extra value included in the conversion. |
| 672 |
* } |
| 673 |
*/ |
| 674 |
public function get_currency_converted_data() { |
| 675 |
$custom_array = array(); |
| 676 |
$_extra = BaseCurrency::convert( 0 ); |
| 677 |
$_rate = BaseCurrency::convert( 1 ) - $_extra; |
| 678 |
$custom_array['cr_active'] = floatval( 1 ) !== floatval( $_rate ) ? true : false; |
| 679 |
$custom_array['cr_rate'] = $_rate; |
| 680 |
$custom_array['cr_extra'] = $_extra; |
| 681 |
|
| 682 |
return $custom_array; |
| 683 |
} |
| 684 |
|
| 685 |
/** |
| 686 |
* Manually revert a converted currency price to its base value. |
| 687 |
* |
| 688 |
* This function uses the conversion rate and extra value from get_currency_converted_data() |
| 689 |
* to calculate the original price before conversion. |
| 690 |
* |
| 691 |
* @since 1.0.4 |
| 692 |
* @param float $price The converted price to revert. |
| 693 |
* @return float The reverted base price. |
| 694 |
*/ |
| 695 |
public function manual_currency_reverted_price( $price ) { |
| 696 |
$currency_data = $this->get_currency_converted_data(); |
| 697 |
if ( $currency_data['cr_active'] ) { |
| 698 |
$cr_rate = isset( $currency_data['cr_rate'] ) ? $currency_data['cr_rate'] : 1; |
| 699 |
$cr_extra = isset( $currency_data['cr_extra'] ) ? $currency_data['cr_extra'] : 0; |
| 700 |
|
| 701 |
if ( floatval( $cr_rate ) == floatval( 0 ) ) { |
| 702 |
return 0; |
| 703 |
} |
| 704 |
|
| 705 |
return ( floatval( $price ) - floatval( $cr_extra ) ) / floatval( $cr_rate ); |
| 706 |
} |
| 707 |
|
| 708 |
return floatval( $price ); |
| 709 |
} |
| 710 |
|
| 711 |
/** |
| 712 |
* Sanitizes the given content using wp_kses with allowed HTML tags. |
| 713 |
* |
| 714 |
* This method applies the 'prad_allowed_html_tags' filter to determine |
| 715 |
* which HTML tags are permitted, and then sanitizes the $prad_blocks content |
| 716 |
* accordingly using wp_kses. |
| 717 |
* |
| 718 |
* @since 1.0.5 |
| 719 |
* |
| 720 |
* @param mixed $prad_blocks The content to be sanitized. |
| 721 |
* @return mixed The sanitized content with only allowed HTML tags. |
| 722 |
*/ |
| 723 |
public function get_wp_kses_content( $prad_blocks ) { |
| 724 |
return $prad_blocks; |
| 725 |
} |
| 726 |
|
| 727 |
/** |
| 728 |
* Get Option Value bypassing cache |
| 729 |
* Inspired By WordPress Core get_option |
| 730 |
* |
| 731 |
* @since v.1.0.7 |
| 732 |
* @param string $option Option Name. |
| 733 |
* @param boolean $default_value option default value. |
| 734 |
* @return mixed |
| 735 |
*/ |
| 736 |
public function get_option_without_cache( $option, $default_value = false ) { |
| 737 |
global $wpdb; |
| 738 |
|
| 739 |
if ( is_scalar( $option ) ) { |
| 740 |
$option = trim( $option ); |
| 741 |
} |
| 742 |
|
| 743 |
if ( empty( $option ) ) { |
| 744 |
return false; |
| 745 |
} |
| 746 |
|
| 747 |
$value = $default_value; |
| 748 |
|
| 749 |
$row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching |
| 750 |
|
| 751 |
if ( is_object( $row ) ) { |
| 752 |
$value = $row->option_value; |
| 753 |
} else { |
| 754 |
return apply_filters( "prad_default_option_{$option}", $default_value, $option ); |
| 755 |
} |
| 756 |
|
| 757 |
return apply_filters( "prad_option_{$option}", maybe_unserialize( $value ), $option ); |
| 758 |
} |
| 759 |
|
| 760 |
/** |
| 761 |
* Add option without adding to the cache |
| 762 |
* Inspired By WordPress Core set_transient |
| 763 |
* |
| 764 |
* @since v.1.0.7 |
| 765 |
* @param string $option option name. |
| 766 |
* @param string $value option value. |
| 767 |
* @param string $autoload whether to load WordPress startup. |
| 768 |
* @return bool |
| 769 |
*/ |
| 770 |
public function add_option_without_cache( $option, $value = '', $autoload = 'yes' ) { |
| 771 |
global $wpdb; |
| 772 |
|
| 773 |
if ( is_scalar( $option ) ) { |
| 774 |
$option = trim( $option ); |
| 775 |
} |
| 776 |
|
| 777 |
if ( empty( $option ) ) { |
| 778 |
return false; |
| 779 |
} |
| 780 |
|
| 781 |
wp_protect_special_option( $option ); |
| 782 |
|
| 783 |
if ( is_object( $value ) ) { |
| 784 |
$value = clone $value; |
| 785 |
} |
| 786 |
|
| 787 |
$value = sanitize_option( $option, $value ); |
| 788 |
|
| 789 |
/* |
| 790 |
* Make sure the option doesn't already exist. |
| 791 |
*/ |
| 792 |
|
| 793 |
if ( apply_filters( "prad_default_option_{$option}", false, $option, false ) !== $this->get_option_without_cache( $option ) ) { |
| 794 |
return false; |
| 795 |
} |
| 796 |
|
| 797 |
$serialized_value = maybe_serialize( $value ); |
| 798 |
$autoload = ( 'no' === $autoload || false === $autoload ) ? 'no' : 'yes'; |
| 799 |
|
| 800 |
$result = $wpdb->query( $wpdb->prepare( "INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE `option_name` = VALUES(`option_name`), `option_value` = VALUES(`option_value`), `autoload` = VALUES(`autoload`)", $option, $serialized_value, $autoload ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching |
| 801 |
if ( ! $result ) { |
| 802 |
return false; |
| 803 |
} |
| 804 |
|
| 805 |
return true; |
| 806 |
} |
| 807 |
|
| 808 |
/** |
| 809 |
* Get Transient Value bypassing cache |
| 810 |
* Inspired By WordPress Core get_transient |
| 811 |
* |
| 812 |
* @since v.1.0.7 |
| 813 |
* @param string $transient Transient Name. |
| 814 |
* @return mixed |
| 815 |
*/ |
| 816 |
public function get_transient_without_cache( $transient ) { |
| 817 |
$transient_option = '_transient_' . $transient; |
| 818 |
$transient_timeout = '_transient_timeout_' . $transient; |
| 819 |
$timeout = $this->get_option_without_cache( $transient_timeout ); |
| 820 |
|
| 821 |
if ( false !== $timeout && $timeout < time() ) { |
| 822 |
delete_option( $transient_option ); |
| 823 |
delete_option( $transient_timeout ); |
| 824 |
$value = false; |
| 825 |
} |
| 826 |
|
| 827 |
if ( ! isset( $value ) ) { |
| 828 |
$value = $this->get_option_without_cache( $transient_option ); |
| 829 |
} |
| 830 |
|
| 831 |
return apply_filters( "prad_transient_{$transient}", $value, $transient ); |
| 832 |
} |
| 833 |
|
| 834 |
/** |
| 835 |
* Set transient without adding to the cache |
| 836 |
* Inspired By WordPress Core set_transient |
| 837 |
* |
| 838 |
* @since v.1.0.7 |
| 839 |
* @param string $transient Transient Name. |
| 840 |
* @param mixed $value Transient Value. |
| 841 |
* @param integer $expiration Time until expiration in seconds. |
| 842 |
* @return bool |
| 843 |
*/ |
| 844 |
public function set_transient_without_cache( $transient, $value, $expiration = 0 ) { |
| 845 |
$expiration = (int) $expiration; |
| 846 |
|
| 847 |
$transient_timeout = '_transient_timeout_' . $transient; |
| 848 |
$transient_option = '_transient_' . $transient; |
| 849 |
|
| 850 |
$result = false; |
| 851 |
|
| 852 |
if ( false === $this->get_option_without_cache( $transient_option ) ) { |
| 853 |
$autoload = 'yes'; |
| 854 |
if ( $expiration ) { |
| 855 |
$autoload = 'no'; |
| 856 |
$this->add_option_without_cache( $transient_timeout, time() + $expiration, 'no' ); |
| 857 |
} |
| 858 |
$result = $this->add_option_without_cache( $transient_option, $value, $autoload ); |
| 859 |
} else { |
| 860 |
/* |
| 861 |
* If expiration is requested, but the transient has no timeout option, |
| 862 |
* delete, then re-create transient rather than update. |
| 863 |
*/ |
| 864 |
$update = true; |
| 865 |
|
| 866 |
if ( $expiration ) { |
| 867 |
if ( false === $this->get_option_without_cache( $transient_timeout ) ) { |
| 868 |
delete_option( $transient_option ); |
| 869 |
$this->add_option_without_cache( $transient_timeout, time() + $expiration, 'no' ); |
| 870 |
$result = $this->add_option_without_cache( $transient_option, $value, 'no' ); |
| 871 |
$update = false; |
| 872 |
} else { |
| 873 |
update_option( $transient_timeout, time() + $expiration ); |
| 874 |
} |
| 875 |
} |
| 876 |
|
| 877 |
if ( $update ) { |
| 878 |
$result = update_option( $transient_option, $value ); |
| 879 |
} |
| 880 |
} |
| 881 |
|
| 882 |
return $result; |
| 883 |
} |
| 884 |
|
| 885 |
/** |
| 886 |
* Generates the HTML for the product block variation section. |
| 887 |
* |
| 888 |
* This function outputs the variation selection UI for a given product block item. |
| 889 |
* |
| 890 |
* @param array $args Arguments containing the product item. |
| 891 |
* @return string The generated HTML for the variation section. |
| 892 |
*/ |
| 893 |
public function generate_products_block_variation_section_html( $args ) { |
| 894 |
$item = $args['item']; |
| 895 |
$allowed_html_tags = apply_filters( 'prad_allowed_html_tags', array() ); |
| 896 |
ob_start(); |
| 897 |
if ( isset( $item->variation ) && $item->variation ) { |
| 898 |
$select_options = ''; |
| 899 |
$product = wc_get_product( $item->id ); |
| 900 |
$available_variations = $product->get_available_variations(); |
| 901 |
foreach ( $available_variations as $variation_data ) { |
| 902 |
$variation_id = $variation_data['variation_id']; |
| 903 |
$variation = wc_get_product( $variation_id ); |
| 904 |
|
| 905 |
if ( $variation && $variation->is_purchasable() && $variation->is_in_stock() ) { |
| 906 |
$variation_attributes = $variation->get_attributes(); |
| 907 |
$option_label = ''; |
| 908 |
$valid_variation = true; |
| 909 |
$i = 0; |
| 910 |
$regular_price = $variation->get_regular_price( '' ); |
| 911 |
$sale_price = $variation->get_sale_price( '' ); |
| 912 |
|
| 913 |
$regular_price = apply_filters( |
| 914 |
'prad_raw_tax_compitable_price', |
| 915 |
array( |
| 916 |
'product_id' => $variation_id, |
| 917 |
'price' => $variation->get_regular_price(), |
| 918 |
'source' => 'product_page', |
| 919 |
) |
| 920 |
); |
| 921 |
$sale_price = apply_filters( |
| 922 |
'prad_raw_tax_compitable_price', |
| 923 |
array( |
| 924 |
'product_id' => $variation_id, |
| 925 |
'price' => $variation->get_sale_price(), |
| 926 |
'source' => 'product_page', |
| 927 |
) |
| 928 |
); |
| 929 |
$price_obj = product_addons()->get_price_object( $regular_price, $sale_price ); |
| 930 |
foreach ( $variation_attributes as $key => $value ) { |
| 931 |
$label = str_replace( '_', ' ', str_replace( 'pa_', '', $key ) ); |
| 932 |
if ( ! empty( $value ) ) { |
| 933 |
$option_label .= ( $i > 0 ? ' , ' : '' ) . ucfirst( $label ) . ' - ' . ucfirst( $value ); |
| 934 |
++$i; |
| 935 |
} else { |
| 936 |
$valid_variation = false; |
| 937 |
} |
| 938 |
} |
| 939 |
if ( $valid_variation ) { |
| 940 |
$option_label = rawurldecode( wp_strip_all_tags( $option_label ) ); |
| 941 |
$select_options .= '<div class="prad-select-option" title="' . esc_attr( $option_label ) . '" value="' . esc_attr( $price_obj['price'] ) . '" data-variation-id="' . esc_attr( $variation_id ) . '" data-pricehtml="' . esc_attr( $price_obj['html'] ) . '">' . esc_html( $option_label ) . '</div>'; |
| 942 |
} |
| 943 |
} |
| 944 |
} |
| 945 |
|
| 946 |
if ( $select_options ) { |
| 947 |
?> |
| 948 |
<div class="prad-product-block-variation-select prad-mt-10"> |
| 949 |
<div class="prad-custom-select prad-w-full prad-product-variation-select-comp"> |
| 950 |
<div class="prad-select-box prad-block-input prad-block-content" readonly="readonly"><div style="max-width: 120px" class="prad-select-box-item prad-mr-12 prad-ellipsis"><?php esc_html_e( 'Select an option', 'product-addons' ); ?></div> <div class="prad-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="8" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m1 1 6 6 6-6"></path></svg></div></div> |
| 951 |
<div class="prad-select-options"> |
| 952 |
<?php echo wp_kses( $select_options, $allowed_html_tags ); ?> |
| 953 |
</div> |
| 954 |
</div> |
| 955 |
</div> |
| 956 |
|
| 957 |
<?php |
| 958 |
} |
| 959 |
} |
| 960 |
return ob_get_clean(); |
| 961 |
} |
| 962 |
|
| 963 |
/** |
| 964 |
* Retrieves product block attributes for a given product ID. |
| 965 |
* |
| 966 |
* Returns an object containing product details such as ID, type, variation status, |
| 967 |
* URL, name, image, regular and sale prices, stock status, and purchasable status. |
| 968 |
* |
| 969 |
* @param int $p_id Product ID. |
| 970 |
* @param bool $var_p Whether the product is a variation. |
| 971 |
* @return object|null Product attributes object or null if not found. |
| 972 |
*/ |
| 973 |
public function get_product_block_product_attr( $p_id, $var_p = false ) { |
| 974 |
$product = wc_get_product( $p_id ); |
| 975 |
if ( $product ) { |
| 976 |
$formatted_variation = wc_get_formatted_variation( $product, true, false, true ); |
| 977 |
$product_value = $product->get_name() . ( $formatted_variation ? ' - ' . $formatted_variation : '' ); |
| 978 |
$data = array( |
| 979 |
'id' => $p_id, |
| 980 |
'type' => 'per_unit', |
| 981 |
'variation' => $var_p, |
| 982 |
'url' => get_permalink( $p_id ), |
| 983 |
'value' => rawurldecode( wp_strip_all_tags( $product_value ) ), |
| 984 |
'img' => wp_get_attachment_url( $product->get_image_id() ), |
| 985 |
'regular' => apply_filters( |
| 986 |
'prad_raw_tax_compitable_price', |
| 987 |
array( |
| 988 |
'product_id' => $p_id, |
| 989 |
'ppType' => 'reg', |
| 990 |
'price' => $product->get_regular_price(), |
| 991 |
'source' => 'product_page', |
| 992 |
) |
| 993 |
), |
| 994 |
'sale' => apply_filters( |
| 995 |
'prad_raw_tax_compitable_price', |
| 996 |
array( |
| 997 |
'product_id' => $p_id, |
| 998 |
'ppType' => 'sale', |
| 999 |
'price' => $product->get_sale_price(), |
| 1000 |
'source' => 'product_page', |
| 1001 |
) |
| 1002 |
), |
| 1003 |
'is_in_stock' => $product->is_in_stock(), |
| 1004 |
'is_purchasable' => $product->is_purchasable(), |
| 1005 |
); |
| 1006 |
return (object) $data; |
| 1007 |
} |
| 1008 |
|
| 1009 |
return null; |
| 1010 |
} |
| 1011 |
|
| 1012 |
/** |
| 1013 |
* Generates a UTM link with specified parameters and configuration. |
| 1014 |
* |
| 1015 |
* This method constructs a URL with UTM parameters for tracking purposes, |
| 1016 |
* optionally including affiliate and hash values, and supports custom UTM configurations. |
| 1017 |
* |
| 1018 |
* @param array $params { |
| 1019 |
* Array of parameters for generating the UTM link. |
| 1020 |
* @type string $url Base URL to append UTM parameters to. |
| 1021 |
* @type string $utmKey Key to select default UTM configuration. |
| 1022 |
* @type string $affiliate Affiliate ID to append as 'ref'. |
| 1023 |
* @type string $hash Hash fragment to append to the URL. |
| 1024 |
* @type array $config Custom UTM configuration array. |
| 1025 |
* } |
| 1026 |
* @return string The generated URL with UTM parameters. |
| 1027 |
*/ |
| 1028 |
public function generate_utm_link( $params ) { |
| 1029 |
// Default UTM configurations. |
| 1030 |
$default_config = array( |
| 1031 |
'example' => array( |
| 1032 |
'source' => 'db-wowaddons-featurename', |
| 1033 |
'medium' => 'block-feature', |
| 1034 |
'campaign' => 'wowaddons-dashboard', |
| 1035 |
), |
| 1036 |
'summer_db' => array( |
| 1037 |
'source' => 'db-wowaddons-notice', |
| 1038 |
'medium' => 'black-friday-sale', |
| 1039 |
'campaign' => 'wowaddons-dashboard', |
| 1040 |
), |
| 1041 |
'final_hour' => array( |
| 1042 |
'source' => 'db-wowaddons-text', |
| 1043 |
'medium' => 'final-hour-sale', |
| 1044 |
'campaign' => 'wowaddons-dashboard', |
| 1045 |
), |
| 1046 |
'massive_sale' => array( |
| 1047 |
'source' => 'db-wowaddons-notice-logo', |
| 1048 |
'medium' => 'massive-sale', |
| 1049 |
'campaign' => 'wowaddons-dashboard', |
| 1050 |
), |
| 1051 |
'flash_sale' => array( |
| 1052 |
'source' => 'db-wowaddons-notice-text', |
| 1053 |
'medium' => 'flash-sale', |
| 1054 |
'campaign' => 'wowaddons-dashboard', |
| 1055 |
), |
| 1056 |
'exclusive_deals' => array( |
| 1057 |
'source' => 'db-wowaddons-notice-logo', |
| 1058 |
'medium' => 'exclusive-deals', |
| 1059 |
'campaign' => 'wowaddons-dashboard', |
| 1060 |
), |
| 1061 |
); |
| 1062 |
|
| 1063 |
// Step 1: Get parameters. |
| 1064 |
$base_url = $params['url'] ?? 'https://www.wpxpo.com/product/wowaddons/'; |
| 1065 |
$utm_key = $params['utmKey'] ?? null; |
| 1066 |
$affiliate = $params['affiliate'] ?? apply_filters( 'prad_affiliate_id', '' ); |
| 1067 |
$hash = $params['hash'] ?? ''; |
| 1068 |
$custom_config = $params['config'] ?? null; |
| 1069 |
|
| 1070 |
$parsed_url = wp_parse_url( $base_url ); |
| 1071 |
$scheme = $parsed_url['scheme'] ?? 'https'; |
| 1072 |
$host = $parsed_url['host'] ?? ''; |
| 1073 |
$path = $parsed_url['path'] ?? ''; |
| 1074 |
$query = array(); |
| 1075 |
|
| 1076 |
// Step 3: Extract existing query params if present. |
| 1077 |
if ( isset( $parsed_url['query'] ) ) { |
| 1078 |
parse_str( $parsed_url['query'], $query ); |
| 1079 |
} |
| 1080 |
|
| 1081 |
// Step 4: Determine config. |
| 1082 |
$utm_config = $custom_config ?? ( $utm_key && isset( $default_config[ $utm_key ] ) ? $default_config[ $utm_key ] : array() ); |
| 1083 |
|
| 1084 |
// Step 5: Add UTM parameters. |
| 1085 |
if ( ! empty( $utm_config ) ) { |
| 1086 |
$query = array_merge( |
| 1087 |
$query, |
| 1088 |
array( |
| 1089 |
'utm_source' => $utm_config['source'], |
| 1090 |
'utm_medium' => $utm_config['medium'], |
| 1091 |
'utm_campaign' => $utm_config['campaign'], |
| 1092 |
) |
| 1093 |
); |
| 1094 |
} |
| 1095 |
|
| 1096 |
// Step 6: Add affiliate if present. |
| 1097 |
if ( $affiliate ) { |
| 1098 |
$query['ref'] = $affiliate; |
| 1099 |
} |
| 1100 |
|
| 1101 |
// Step 7: Reconstruct URL. |
| 1102 |
$final_url = $scheme . '://' . $host . $path; |
| 1103 |
|
| 1104 |
if ( ! empty( $query ) ) { |
| 1105 |
$final_url .= '?' . http_build_query( $query ); |
| 1106 |
} |
| 1107 |
|
| 1108 |
if ( $hash ) { |
| 1109 |
$final_url .= '#' . $hash; |
| 1110 |
} |
| 1111 |
|
| 1112 |
return $final_url; |
| 1113 |
} |
| 1114 |
|
| 1115 |
/** |
| 1116 |
* Get WOW Products Details |
| 1117 |
* |
| 1118 |
* @return array |
| 1119 |
*/ |
| 1120 |
public function get_wow_products_details() { |
| 1121 |
return array( |
| 1122 |
'products' => array( |
| 1123 |
'wow_shipping' => file_exists( WP_PLUGIN_DIR . '/wow-table-rate-shipping/wow-table-rate-shipping.php' ), |
| 1124 |
'post_x' => file_exists( WP_PLUGIN_DIR . '/ultimate-post/ultimate-post.php' ), |
| 1125 |
'wow_store' => file_exists( WP_PLUGIN_DIR . '/product-blocks/product-blocks.php' ), |
| 1126 |
'wow_optin' => file_exists( WP_PLUGIN_DIR . '/optin/optin.php' ), |
| 1127 |
'wow_revenue' => file_exists( WP_PLUGIN_DIR . '/revenue/revenue.php' ), |
| 1128 |
'wholesale_x' => file_exists( WP_PLUGIN_DIR . '/wholesalex/wholesalex.php' ), |
| 1129 |
), |
| 1130 |
'products_active' => array( |
| 1131 |
'wow_shipping' => defined( 'WTRS_VER' ), |
| 1132 |
'post_x' => defined( 'ULTP_VER' ), |
| 1133 |
'wow_store' => defined( 'WOPB_VER' ), |
| 1134 |
'wow_optin' => defined( 'OPTN_VERSION' ), |
| 1135 |
'wow_revenue' => defined( 'REVENUE_VER' ), |
| 1136 |
'wholesale_x' => defined( 'WHOLESALEX_VER' ), |
| 1137 |
), |
| 1138 |
); |
| 1139 |
} |
| 1140 |
|
| 1141 |
/** |
| 1142 |
* Move or copy files from temp folder to on_cart folder. |
| 1143 |
* |
| 1144 |
* @param array $src_files Absolute file paths (from temp folder). |
| 1145 |
* @param string $_from Source folder name ('temp' or 'order_placed'). |
| 1146 |
* @param bool $delete Whether to delete original files (true = move, false = copy). |
| 1147 |
* |
| 1148 |
* @return array List of processed files with new paths & URLs. |
| 1149 |
*/ |
| 1150 |
public function prad_move_uploadblock_files( array $src_files, string $_from = 'temp', bool $delete = false ) { |
| 1151 |
$upload_dir = wp_upload_dir(); |
| 1152 |
|
| 1153 |
$_to = 'order_placed'; |
| 1154 |
if ( 'order_placed' === $_from ) { |
| 1155 |
$_to = 'order_completed'; |
| 1156 |
} |
| 1157 |
|
| 1158 |
$from_dir = $upload_dir['basedir'] . '/prad_option_files/' . $_from; |
| 1159 |
$to_dir = $upload_dir['basedir'] . '/prad_option_files/' . $_to; |
| 1160 |
|
| 1161 |
// Make sure destination folder exists |
| 1162 |
if ( ! file_exists( $to_dir ) ) { |
| 1163 |
wp_mkdir_p( $to_dir ); |
| 1164 |
} |
| 1165 |
|
| 1166 |
// Make sure temp folder exists |
| 1167 |
if ( ! file_exists( $from_dir ) ) { |
| 1168 |
wp_mkdir_p( $from_dir ); |
| 1169 |
} |
| 1170 |
|
| 1171 |
$processed = array(); |
| 1172 |
|
| 1173 |
foreach ( $src_files as $src ) { |
| 1174 |
// Extract filename with extension. |
| 1175 |
$filename = basename( $src ); |
| 1176 |
|
| 1177 |
$source_path = $from_dir . '/' . $filename; |
| 1178 |
$destination_path = $to_dir . '/' . $filename; |
| 1179 |
|
| 1180 |
// Skip if source file doesn't exist. |
| 1181 |
if ( ! file_exists( $source_path ) ) { |
| 1182 |
continue; |
| 1183 |
} |
| 1184 |
|
| 1185 |
if ( 'on_cart' !== $_to && file_exists( $destination_path ) ) { |
| 1186 |
$file_info = pathinfo( $filename ); |
| 1187 |
$name = $file_info['filename']; |
| 1188 |
$ext = isset( $file_info['extension'] ) ? '.' . $file_info['extension'] : ''; |
| 1189 |
$counter = 1; |
| 1190 |
|
| 1191 |
while ( file_exists( $destination_path ) ) { |
| 1192 |
$destination_path = $to_dir . '/' . $name . '-' . $counter . $ext; |
| 1193 |
++$counter; |
| 1194 |
} |
| 1195 |
} |
| 1196 |
|
| 1197 |
if ( copy( $source_path, $destination_path ) ) { |
| 1198 |
if ( $delete ) { |
| 1199 |
wp_delete_file( $source_path ); |
| 1200 |
} |
| 1201 |
|
| 1202 |
$processed[] = array( |
| 1203 |
'updated_src' => array( |
| 1204 |
'prev_src' => $upload_dir['baseurl'] . '/prad_option_files/' . $_from . '/' . basename( $source_path ), |
| 1205 |
'curr_src' => $upload_dir['baseurl'] . '/prad_option_files/' . $_to . '/' . basename( $destination_path ), |
| 1206 |
'curr_name' => basename( $destination_path ), |
| 1207 |
), |
| 1208 |
'file' => $destination_path, |
| 1209 |
'url' => $upload_dir['baseurl'] . '/prad_option_files/' . $_to . '/' . basename( $destination_path ), |
| 1210 |
); |
| 1211 |
} |
| 1212 |
} |
| 1213 |
|
| 1214 |
return $processed; |
| 1215 |
} |
| 1216 |
|
| 1217 |
/** |
| 1218 |
* Safe stripslashes that handles both strings and arrays. |
| 1219 |
* |
| 1220 |
* This function safely applies stripslashes to a value, whether it's a string or an array. |
| 1221 |
* If the value is already an array, it returns the array as-is. |
| 1222 |
* If the value is a string, it applies stripslashes and returns the result. |
| 1223 |
* |
| 1224 |
* @since 1.0.0 |
| 1225 |
* @param mixed $value The value to process (string or array). |
| 1226 |
* @return mixed The processed value. |
| 1227 |
*/ |
| 1228 |
public function safe_stripslashes( $value ) { |
| 1229 |
if ( is_array( $value ) ) { |
| 1230 |
return $value; |
| 1231 |
} |
| 1232 |
|
| 1233 |
if ( is_string( $value ) ) { |
| 1234 |
return stripslashes( $value ); |
| 1235 |
} |
| 1236 |
|
| 1237 |
return $value; |
| 1238 |
} |
| 1239 |
|
| 1240 |
/** |
| 1241 |
* Retrieves the list of allowed file types for uploads. |
| 1242 |
* |
| 1243 |
* This function returns an array of file types that are permitted to be uploaded |
| 1244 |
* through the product addons functionality. |
| 1245 |
* |
| 1246 |
* @return array List of allowed file types for uploads. |
| 1247 |
*/ |
| 1248 |
public function prad_get_upload_allowed_file_types( $extras = array() ) { |
| 1249 |
|
| 1250 |
$allowed_types = array( |
| 1251 |
'png' => 'image/png', |
| 1252 |
'jpg' => 'image/jpeg', |
| 1253 |
'jpeg' => 'image/jpeg', |
| 1254 |
'pdf' => 'application/pdf', |
| 1255 |
'csv' => 'text/csv', |
| 1256 |
'doc' => 'application/msword', |
| 1257 |
'txt' => 'text/plain', |
| 1258 |
'ppt' => 'application/vnd.ms-powerpoint', |
| 1259 |
'heic' => 'image/heic', |
| 1260 |
'svg' => 'image/svg+xml', |
| 1261 |
'ai' => 'application/pdf', |
| 1262 |
'psd' => 'image/vnd.adobe.photoshop', |
| 1263 |
'eps' => 'application/postscript', |
| 1264 |
'cdr' => 'application/vnd.corel-draw', |
| 1265 |
'gpx' => 'text/xml', |
| 1266 |
); |
| 1267 |
|
| 1268 |
if ( ! empty( $extras ) && is_array( $extras ) ) { |
| 1269 |
$allowed_types = array_merge( $allowed_types, $extras ); |
| 1270 |
} |
| 1271 |
|
| 1272 |
return apply_filters( 'prad_upload_field_allowed_file_types', $allowed_types ); |
| 1273 |
} |
| 1274 |
|
| 1275 |
/** |
| 1276 |
* Retrieves global WooCommerce product attributes and their terms. |
| 1277 |
* |
| 1278 |
* This function fetches all global attributes defined in WooCommerce |
| 1279 |
* and their associated terms, returning them in a structured array. |
| 1280 |
* |
| 1281 |
* @return array An associative array of attributes and their terms. |
| 1282 |
*/ |
| 1283 |
public function prad_get_attributes() { |
| 1284 |
$global_attrs = wc_get_attribute_taxonomies(); |
| 1285 |
|
| 1286 |
$attrs = array(); |
| 1287 |
foreach ( $global_attrs as $attribute ) { |
| 1288 |
$attr_name = $attribute->attribute_name; |
| 1289 |
$terms = get_terms( |
| 1290 |
array( |
| 1291 |
'taxonomy' => wc_attribute_taxonomy_name( $attr_name ), |
| 1292 |
'hide_empty' => false, |
| 1293 |
) |
| 1294 |
); |
| 1295 |
|
| 1296 |
foreach ( $terms as $term ) { |
| 1297 |
$attrs_ops[ $attr_name ][] = array( |
| 1298 |
'value' => (int) $term->term_id, |
| 1299 |
'slug' => $term->slug, |
| 1300 |
'label' => $term->name, |
| 1301 |
); |
| 1302 |
} |
| 1303 |
|
| 1304 |
$attrs[ 'pa_' . $attr_name ] = array( |
| 1305 |
'label' => $attribute->attribute_label, |
| 1306 |
'options' => isset( $attrs_ops[ $attr_name ] ) ? $attrs_ops[ $attr_name ] : array(), |
| 1307 |
); |
| 1308 |
} |
| 1309 |
|
| 1310 |
return $attrs; |
| 1311 |
} |
| 1312 |
|
| 1313 |
/** |
| 1314 |
* Retrieves product option IDs based on global, product-specific, and term assignments. |
| 1315 |
* |
| 1316 |
* This function merges options assigned globally, per product, and by product terms, |
| 1317 |
* then excludes any options marked as excluded. |
| 1318 |
* |
| 1319 |
* @param int $product_id The product ID. |
| 1320 |
* |
| 1321 |
* @return array An array of option IDs assigned to the product. |
| 1322 |
*/ |
| 1323 |
public function get_product_option_ids( int $product_id ): array { |
| 1324 |
$option_all = $this->get_json_option( 'prad_option_assign_all', array() ); |
| 1325 |
|
| 1326 |
// Get options assigned directly to this product. |
| 1327 |
$option_product = $this->get_json_meta( $product_id, 'prad_product_assigned_meta_inc', array() ); |
| 1328 |
|
| 1329 |
// Get options excluded from this product. |
| 1330 |
$option_exclude = $this->get_json_meta( $product_id, 'prad_product_assigned_meta_exc', array() ); |
| 1331 |
|
| 1332 |
// Get options from product terms (categories, tags, brands). |
| 1333 |
$option_terms_inc = $this->get_product_term_options( $product_id, 'prad_term_assigned_meta_inc' ); |
| 1334 |
$option_terms_exc = $this->get_product_term_options( $product_id, 'prad_term_assigned_meta_exc' ); |
| 1335 |
|
| 1336 |
// Merge and filter. |
| 1337 |
$merged = array_unique( array_merge( $option_all, $option_terms_inc, $option_product ) ); |
| 1338 |
$option_ids = array_diff( $merged, $option_exclude, $option_terms_exc ); |
| 1339 |
|
| 1340 |
// Sort for consistency. |
| 1341 |
sort( $option_ids ); |
| 1342 |
|
| 1343 |
return apply_filters( 'prad_product_option_ids', $option_ids, $product_id ); |
| 1344 |
} |
| 1345 |
|
| 1346 |
/** |
| 1347 |
* Retrieves product options from terms (categories, tags, brands). |
| 1348 |
* |
| 1349 |
* @param int $product_id The product ID. |
| 1350 |
* @param string $meta_key The meta key to retrieve ('prad_term_assigned_meta_inc' or 'prad_term_assigned_meta_exc'). |
| 1351 |
* |
| 1352 |
* @return array An array of option IDs from product terms. |
| 1353 |
*/ |
| 1354 |
private function get_product_term_options( int $product_id, string $meta_key ) { |
| 1355 |
$option_terms = array(); |
| 1356 |
if ( 'prad_term_assigned_meta_exc' === $meta_key && ! $this->is_lc_active() ) { |
| 1357 |
return $option_terms; |
| 1358 |
} |
| 1359 |
|
| 1360 |
$taxonomies = array( 'product_cat', 'product_tag', 'product_brand' ); |
| 1361 |
if ( 'prad_term_assigned_meta_exc' === $meta_key ) { |
| 1362 |
$taxonomies = array( 'product_cat' ); |
| 1363 |
} |
| 1364 |
|
| 1365 |
foreach ( $taxonomies as $taxonomy ) { |
| 1366 |
$terms = get_the_terms( $product_id, $taxonomy ); |
| 1367 |
if ( $terms && ! is_wp_error( $terms ) ) { |
| 1368 |
foreach ( $terms as $term ) { |
| 1369 |
$term_options = $this->get_json_term_meta( $term->term_id, $meta_key, array() ); |
| 1370 |
|
| 1371 |
if ( is_array( $term_options ) ) { |
| 1372 |
$option_terms = array_unique( array_merge( $option_terms, $term_options ) ); |
| 1373 |
} |
| 1374 |
} |
| 1375 |
} |
| 1376 |
} |
| 1377 |
|
| 1378 |
return $option_terms; |
| 1379 |
} |
| 1380 |
|
| 1381 |
/** |
| 1382 |
* Retrieves a JSON-encoded option and decodes it. |
| 1383 |
* |
| 1384 |
* @param string $option_name The option name. |
| 1385 |
* @param array $def The default value if option is not found. |
| 1386 |
* |
| 1387 |
* @return array The decoded option value or default. |
| 1388 |
*/ |
| 1389 |
private function get_json_option( string $option_name, array $def = array() ): array { |
| 1390 |
$value = get_option( $option_name, '[]' ); |
| 1391 |
$decoded = json_decode( product_addons()->safe_stripslashes( $value ), true ); |
| 1392 |
|
| 1393 |
return is_array( $decoded ) ? $decoded : $def; |
| 1394 |
} |
| 1395 |
|
| 1396 |
/** |
| 1397 |
* Retrieves a JSON-encoded post meta and decodes it. |
| 1398 |
* |
| 1399 |
* @param int $post_id The post ID. |
| 1400 |
* @param string $meta_key The meta key. |
| 1401 |
* @param array $def The default value if meta is not found. |
| 1402 |
* |
| 1403 |
* @return array The decoded meta value or default. |
| 1404 |
*/ |
| 1405 |
private function get_json_meta( int $post_id, string $meta_key, array $def = array() ): array { |
| 1406 |
$value = get_post_meta( $post_id, $meta_key, true ); |
| 1407 |
|
| 1408 |
if ( empty( $value ) ) { |
| 1409 |
return $def; |
| 1410 |
} |
| 1411 |
|
| 1412 |
$decoded = json_decode( product_addons()->safe_stripslashes( $value ), true ); |
| 1413 |
|
| 1414 |
return is_array( $decoded ) ? $decoded : $def; |
| 1415 |
} |
| 1416 |
|
| 1417 |
/** |
| 1418 |
* Retrieves a JSON-encoded term meta and decodes it. |
| 1419 |
* |
| 1420 |
* @param int $term_id The term ID. |
| 1421 |
* @param string $meta_key The meta key. |
| 1422 |
* @param array $def The default value if meta is not found. |
| 1423 |
* |
| 1424 |
* @return array The decoded meta value or default. |
| 1425 |
*/ |
| 1426 |
private function get_json_term_meta( int $term_id, string $meta_key, array $def = array() ): array { |
| 1427 |
$value = get_term_meta( $term_id, $meta_key, true ); |
| 1428 |
|
| 1429 |
if ( empty( $value ) ) { |
| 1430 |
return $def; |
| 1431 |
} |
| 1432 |
|
| 1433 |
$decoded = json_decode( product_addons()->safe_stripslashes( $value ), true ); |
| 1434 |
|
| 1435 |
return is_array( $decoded ) ? $decoded : $def; |
| 1436 |
} |
| 1437 |
} |
| 1438 |
|