| 1 |
<?php |
| 2 |
|
| 3 |
defined( 'ABSPATH' ) || exit; |
| 4 |
|
| 5 |
use Eventin\Settings; |
| 6 |
use Eventin\Validation\Validator; |
| 7 |
use Etn\Core\Event\Event_Model; |
| 8 |
use Etn\Utils\Helper; |
| 9 |
use SureCart\Support\Currency; |
| 10 |
|
| 11 |
if ( ! function_exists( 'etn_safe_decode' ) ) { |
| 12 |
/** |
| 13 |
* Safely unserialize data, preventing PHP Object Injection. |
| 14 |
* |
| 15 |
* Uses allowed_classes => false to block instantiation of |
| 16 |
* PHP objects during unserialization. |
| 17 |
* |
| 18 |
* @since 4.1.4 |
| 19 |
* @param mixed $data The data to unserialize. |
| 20 |
* @return mixed The unserialized data (no objects), or the original data if not serialized. |
| 21 |
*/ |
| 22 |
function etn_safe_decode( $data ) { |
| 23 |
if ( ! is_string( $data ) ) { |
| 24 |
return $data; |
| 25 |
} |
| 26 |
|
| 27 |
if ( ! is_serialized( $data ) ) { |
| 28 |
return $data; |
| 29 |
} |
| 30 |
|
| 31 |
return @unserialize( $data, [ 'allowed_classes' => false ] ); |
| 32 |
} |
| 33 |
} |
| 34 |
|
| 35 |
if ( ! function_exists( 'etn_sanitize_array_input' ) ) { |
| 36 |
/** |
| 37 |
* Sanitize input to prevent PHP Object Injection. |
| 38 |
* |
| 39 |
* This function rejects serialized strings that could contain malicious PHP objects. |
| 40 |
* Arrays and non-serialized strings are passed through for further processing. |
| 41 |
* |
| 42 |
* @since 4.1.2 |
| 43 |
* @param mixed $input The input to sanitize. |
| 44 |
* @return array|string Returns array if input is array, string if input is non-serialized string, empty array otherwise. |
| 45 |
*/ |
| 46 |
function etn_sanitize_array_input( $input ) { |
| 47 |
// If it's already an array, return it |
| 48 |
if ( is_array( $input ) ) { |
| 49 |
return $input; |
| 50 |
} |
| 51 |
// If it's a serialized string, reject it to prevent PHP Object Injection |
| 52 |
if ( is_string( $input ) && is_serialized( $input ) ) { |
| 53 |
return []; |
| 54 |
} |
| 55 |
// Return non-serialized strings as-is (safe for further processing like CSV parsing) |
| 56 |
if ( is_string( $input ) ) { |
| 57 |
return $input; |
| 58 |
} |
| 59 |
// Return empty array for other non-array input types |
| 60 |
return []; |
| 61 |
} |
| 62 |
} |
| 63 |
|
| 64 |
if ( ! function_exists( 'etn_sanitize_faq_array' ) ) { |
| 65 |
/** |
| 66 |
* Sanitize an array of event FAQ items at input/save time. |
| 67 |
* |
| 68 |
* Each FAQ item carries a plain-text title, rich-text content, and an |
| 69 |
* enabled flag. Titles are reduced to plain text, content is passed through |
| 70 |
* wp_kses_post() so that scripts and event-handler attributes (e.g. |
| 71 |
* onerror=) are stripped before storage, and the enabled flag is cast to a |
| 72 |
* boolean. This neutralizes stored XSS at the source rather than relying on |
| 73 |
* every downstream render context to escape (CVE-2026-12924). |
| 74 |
* |
| 75 |
* @since 4.1.17 |
| 76 |
* @param mixed $faqs The raw FAQ input (expected: array of FAQ items). |
| 77 |
* @return array The sanitized FAQ array (empty array for non-array input). |
| 78 |
*/ |
| 79 |
function etn_sanitize_faq_array( $faqs ) { |
| 80 |
if ( ! is_array( $faqs ) ) { |
| 81 |
return []; |
| 82 |
} |
| 83 |
|
| 84 |
return array_map( |
| 85 |
function ( $faq ) { |
| 86 |
if ( ! is_array( $faq ) ) { |
| 87 |
return is_string( $faq ) ? wp_kses_post( $faq ) : ''; |
| 88 |
} |
| 89 |
|
| 90 |
if ( isset( $faq['etn_faq_title'] ) ) { |
| 91 |
$faq['etn_faq_title'] = sanitize_text_field( $faq['etn_faq_title'] ); |
| 92 |
} |
| 93 |
|
| 94 |
if ( isset( $faq['etn_faq_content'] ) ) { |
| 95 |
$faq['etn_faq_content'] = wp_kses_post( $faq['etn_faq_content'] ); |
| 96 |
} |
| 97 |
|
| 98 |
if ( array_key_exists( 'etn_faq_enabled', $faq ) ) { |
| 99 |
$faq['etn_faq_enabled'] = filter_var( $faq['etn_faq_enabled'], FILTER_VALIDATE_BOOLEAN ); |
| 100 |
} |
| 101 |
|
| 102 |
return $faq; |
| 103 |
}, |
| 104 |
$faqs |
| 105 |
); |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
if ( ! function_exists( 'etn_array_csv_column' ) ) { |
| 110 |
/** |
| 111 |
* Convert array to CSV column |
| 112 |
* |
| 113 |
* @param array $data |
| 114 |
* |
| 115 |
* @return string |
| 116 |
*/ |
| 117 |
function etn_array_csv_column( $data = [] ) { |
| 118 |
$result_string = ''; |
| 119 |
|
| 120 |
foreach ( $data as $data_key => $value ) { |
| 121 |
if ( ! is_array( $value ) ) { |
| 122 |
return etn_is_associative_array( $data ) ? etn_single_array_csv_column( $data ) : implode( ',', $data ); |
| 123 |
} |
| 124 |
|
| 125 |
if ( etn_is_associative_array( $value ) ) { |
| 126 |
$valueString = etn_single_array_csv_column( $value ); |
| 127 |
$result_string .= rtrim( $valueString, ', ' ) . '|'; |
| 128 |
} else { |
| 129 |
$result_string .= implode( ',', $value ) . '|'; |
| 130 |
} |
| 131 |
} |
| 132 |
|
| 133 |
// Remove the trailing '|' |
| 134 |
$result_string = rtrim( $result_string, '|' ); |
| 135 |
|
| 136 |
return $result_string; |
| 137 |
} |
| 138 |
} |
| 139 |
|
| 140 |
if ( ! function_exists( 'etn_is_associative_array' ) ) { |
| 141 |
/** |
| 142 |
* Check an associative array or not |
| 143 |
* |
| 144 |
* @param array $array |
| 145 |
* |
| 146 |
* @return bool |
| 147 |
*/ |
| 148 |
function etn_is_associative_array( $array ) { |
| 149 |
return is_array( $array ) && count( array_filter( array_keys( $array ), 'is_string' ) ) > 0; |
| 150 |
} |
| 151 |
} |
| 152 |
|
| 153 |
if ( ! function_exists( 'etn_single_array_csv_column' ) ) { |
| 154 |
/** |
| 155 |
* Convert single array to csv column |
| 156 |
* |
| 157 |
* @param array $data |
| 158 |
* |
| 159 |
* @return string |
| 160 |
*/ |
| 161 |
function etn_single_array_csv_column( $data ) { |
| 162 |
if ( ! is_array( $data ) ) { |
| 163 |
return false; |
| 164 |
} |
| 165 |
|
| 166 |
$result_string = ''; |
| 167 |
|
| 168 |
foreach ( $data as $key => $value ) { |
| 169 |
if ( is_array( $value ) ) { |
| 170 |
$result_string .= implode( ',', $value ); |
| 171 |
} else { |
| 172 |
$result_string .= "$key:$value,"; |
| 173 |
} |
| 174 |
} |
| 175 |
|
| 176 |
return rtrim( $result_string, ',' ); |
| 177 |
} |
| 178 |
} |
| 179 |
|
| 180 |
if ( ! function_exists( 'etn_csv_column_array' ) ) { |
| 181 |
/** |
| 182 |
* Convert CSV column to array |
| 183 |
* |
| 184 |
* @param string $csvColumn |
| 185 |
* |
| 186 |
* @return array|bool |
| 187 |
*/ |
| 188 |
function etn_csv_column_array( $csv_column, $separator = '|' ) { |
| 189 |
// Explode the CSV column by '|' to get individual array elements |
| 190 |
if ( strpos( $csv_column, $separator ) !== false ) { |
| 191 |
return etn_csv_column_multi_dimension_array( $csv_column ); |
| 192 |
} |
| 193 |
|
| 194 |
return etn_csv_column_single_array( $csv_column ); |
| 195 |
} |
| 196 |
} |
| 197 |
|
| 198 |
if ( ! function_exists( 'etn_csv_column_multi_dimension_array' ) ) { |
| 199 |
/** |
| 200 |
* Convert CSV column to multi dimensional array |
| 201 |
* |
| 202 |
* @param string $csv_column |
| 203 |
* @param string $separator |
| 204 |
* |
| 205 |
* @return array |
| 206 |
*/ |
| 207 |
function etn_csv_column_multi_dimension_array( $csv_column, $separator = '|' ) { |
| 208 |
$array_strings = explode( $separator, $csv_column ); |
| 209 |
$result_array = []; |
| 210 |
|
| 211 |
foreach ( $array_strings as $array_string ) { |
| 212 |
// Add the temporary array to the result array |
| 213 |
$result_array[] = etn_csv_column_single_array( $array_string ); |
| 214 |
} |
| 215 |
|
| 216 |
return $result_array; |
| 217 |
} |
| 218 |
} |
| 219 |
|
| 220 |
if ( ! function_exists( 'etn_csv_column_single_array' ) ) { |
| 221 |
/** |
| 222 |
* Convert CSV column to multi dimensional array |
| 223 |
* |
| 224 |
* @param string $csv_column |
| 225 |
* @param string $separator |
| 226 |
* |
| 227 |
* @return array |
| 228 |
*/ |
| 229 |
function etn_csv_column_single_array( $csv_column, $separator = ',' ) { |
| 230 |
$temp_array = []; |
| 231 |
|
| 232 |
if ( false !== strpos( $csv_column, ':' ) ) { |
| 233 |
$csv_column = explode( $separator, $csv_column ); |
| 234 |
|
| 235 |
foreach ( $csv_column as $pair ) { |
| 236 |
// Explode key-value pairs by ':' and populate the temporary array |
| 237 |
list( $key, $value ) = explode( ':', $pair ); |
| 238 |
$temp_array[$key] = $value; |
| 239 |
} |
| 240 |
|
| 241 |
return $temp_array; |
| 242 |
} |
| 243 |
|
| 244 |
return explode( $separator, $csv_column ); |
| 245 |
} |
| 246 |
} |
| 247 |
|
| 248 |
if ( ! function_exists( 'etn_is_request' ) ) { |
| 249 |
/** |
| 250 |
* What type of request is this? |
| 251 |
* |
| 252 |
* @param string $type admin, ajax, cron or frontend. |
| 253 |
* |
| 254 |
* @return bool |
| 255 |
*/ |
| 256 |
function etn_is_request( $type ) { |
| 257 |
switch ( $type ) { |
| 258 |
case 'admin': |
| 259 |
return is_admin(); |
| 260 |
|
| 261 |
case 'ajax': |
| 262 |
return defined( 'DOING_AJAX' ); |
| 263 |
|
| 264 |
case 'rest': |
| 265 |
return defined( 'REST_REQUEST' ); |
| 266 |
|
| 267 |
case 'cron': |
| 268 |
return defined( 'DOING_CRON' ); |
| 269 |
|
| 270 |
case 'frontend': |
| 271 |
return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ); |
| 272 |
} |
| 273 |
} |
| 274 |
} |
| 275 |
|
| 276 |
if ( ! function_exists( 'etn_get_locale_data' ) ) { |
| 277 |
/** |
| 278 |
* Get locale data |
| 279 |
* |
| 280 |
* @return array |
| 281 |
*/ |
| 282 |
function etn_get_locale_data() { |
| 283 |
$localize_vars = include Wpeventin::plugin_dir() . 'utils/locale/vars.php'; |
| 284 |
$localize_static = include Wpeventin::plugin_dir() . 'utils/locale/static.php'; |
| 285 |
|
| 286 |
$data = array_merge( $localize_static, $localize_vars ); |
| 287 |
|
| 288 |
return apply_filters( 'etn_locale_data', $data ); |
| 289 |
} |
| 290 |
} |
| 291 |
|
| 292 |
if ( ! function_exists( 'etn_permision_error' ) ) { |
| 293 |
/** |
| 294 |
* Rest api error message |
| 295 |
* |
| 296 |
* @param string $message |
| 297 |
* |
| 298 |
* @return \WP_REST_Response |
| 299 |
*/ |
| 300 |
function etn_permision_error( $message = '' ) { |
| 301 |
if ( ! $message ) { |
| 302 |
$message = __( 'Sorry, you are not allowed to do that.', 'eventin' ); |
| 303 |
} |
| 304 |
|
| 305 |
$data = [ |
| 306 |
'code' => 'rest_forbidden', |
| 307 |
'message' => 'Sorry, you are not allowed to do that.', |
| 308 |
'data' => [ |
| 309 |
'status' => 403, |
| 310 |
], |
| 311 |
]; |
| 312 |
|
| 313 |
return new WP_REST_Response( $data, 401 ); |
| 314 |
} |
| 315 |
} |
| 316 |
|
| 317 |
if ( ! function_exists( 'etn_parse_block_content' ) ) { |
| 318 |
/** |
| 319 |
* Parses dynamic blocks out of `post_content` and re-renders them. |
| 320 |
* |
| 321 |
* @param string $content |
| 322 |
* |
| 323 |
* @return string |
| 324 |
*/ |
| 325 |
function etn_parse_block_content( $content ) { |
| 326 |
return do_blocks( $content ); |
| 327 |
} |
| 328 |
} |
| 329 |
|
| 330 |
if ( ! function_exists( 'etn_validate' ) ) { |
| 331 |
/** |
| 332 |
* Validate user input |
| 333 |
* |
| 334 |
* @param array $request |
| 335 |
* @param array $rules |
| 336 |
* |
| 337 |
* @return bool | WP_Error |
| 338 |
*/ |
| 339 |
function etn_validate( $request, $rules ) { |
| 340 |
$validator = new Validator( $request ); |
| 341 |
|
| 342 |
$validator->set_rules( $rules ); |
| 343 |
|
| 344 |
if ( ! $validator->validate() ) { |
| 345 |
return $validator->get_error(); |
| 346 |
} |
| 347 |
|
| 348 |
return true; |
| 349 |
} |
| 350 |
} |
| 351 |
|
| 352 |
if ( ! function_exists( 'etn_get_option' ) ) { |
| 353 |
/** |
| 354 |
* Get option for eventin |
| 355 |
* |
| 356 |
* @since 1.0.0 |
| 357 |
* @return mixed |
| 358 |
*/ |
| 359 |
function etn_get_option( $key = '', $default = false ) { |
| 360 |
$value = Settings::get( $key ); |
| 361 |
|
| 362 |
if ( ! $value ) { |
| 363 |
return $default; |
| 364 |
} |
| 365 |
|
| 366 |
return $value; |
| 367 |
} |
| 368 |
} |
| 369 |
|
| 370 |
if ( ! function_exists( 'etn_update_option' ) ) { |
| 371 |
|
| 372 |
/** |
| 373 |
* Update option |
| 374 |
* |
| 375 |
* @param string $key |
| 376 |
* |
| 377 |
* @since 1.0.0 |
| 378 |
* |
| 379 |
* @return boolean |
| 380 |
*/ |
| 381 |
function etn_update_option( $key = '', $value = false ) { |
| 382 |
if ( ! $key ) { |
| 383 |
return false; |
| 384 |
} |
| 385 |
|
| 386 |
return Settings::update( [ |
| 387 |
$key => $value, |
| 388 |
] ); |
| 389 |
} |
| 390 |
} |
| 391 |
|
| 392 |
if ( ! function_exists( 'etn_parse_event_datetime' ) ) { |
| 393 |
/** |
| 394 |
* Build a DateTime from a stored event date + time pair. |
| 395 |
* |
| 396 |
* Normalizes non-standard time shapes ("8h00", "6.45", "6 45") to "HH:MM" |
| 397 |
* before construction, and falls back to date-only at midnight rather than |
| 398 |
* fataling on unparseable input. |
| 399 |
* |
| 400 |
* @param string $date Date string (Y-m-d). |
| 401 |
* @param string $time Time string. May be empty or malformed. |
| 402 |
* @param \DateTimeZone|string|null $timezone DateTimeZone instance, IANA string, or null for wp_timezone(). |
| 403 |
* |
| 404 |
* @return \DateTime |
| 405 |
*/ |
| 406 |
function etn_parse_event_datetime( $date, $time = '', $timezone = null ) { |
| 407 |
if ( $timezone instanceof \DateTimeZone ) { |
| 408 |
$tz = $timezone; |
| 409 |
} elseif ( is_string( $timezone ) && $timezone !== '' ) { |
| 410 |
try { |
| 411 |
$tz = new \DateTimeZone( $timezone ); |
| 412 |
} catch ( \Exception $e ) { |
| 413 |
$tz = wp_timezone(); |
| 414 |
} |
| 415 |
} else { |
| 416 |
$tz = wp_timezone(); |
| 417 |
} |
| 418 |
|
| 419 |
$time = is_string( $time ) ? trim( $time ) : ''; |
| 420 |
|
| 421 |
// Normalize "digits + non-digits + digits" (e.g. "8h00", "6.45", "6 45") → "08:00" / "06:45". |
| 422 |
// Skip values that already contain a colon — "9:00 PM" / "21:00" are parsed natively by |
| 423 |
// \DateTime, and reformatting them here would drop the AM/PM meridiem (turning every PM |
| 424 |
// time into the morning). Preserve any meridiem for the compact formats that do get normalized. |
| 425 |
if ( $time && false === strpos( $time, ':' ) |
| 426 |
&& preg_match( '/^(\d{1,2})\D+(\d{1,2})\s*([AaPp][Mm])?/', $time, $m ) ) { |
| 427 |
$time = sprintf( '%02d:%02d', (int) $m[1], (int) $m[2] ); |
| 428 |
if ( ! empty( $m[3] ) ) { |
| 429 |
$time .= ' ' . strtoupper( $m[3] ); |
| 430 |
} |
| 431 |
} |
| 432 |
|
| 433 |
$date_time_string = trim( $date . ' ' . $time ); |
| 434 |
|
| 435 |
try { |
| 436 |
return new \DateTime( $date_time_string, $tz ); |
| 437 |
} catch ( \Exception $e ) { |
| 438 |
try { |
| 439 |
return new \DateTime( (string) $date, $tz ); |
| 440 |
} catch ( \Exception $e2 ) { |
| 441 |
return new \DateTime( 'now', $tz ); |
| 442 |
} |
| 443 |
} |
| 444 |
} |
| 445 |
} |
| 446 |
|
| 447 |
if ( ! function_exists( 'etn_is_ticket_sale_end' ) ) { |
| 448 |
/** |
| 449 |
* Check an event has attendees or not |
| 450 |
* |
| 451 |
* @param string $end_date_time Event ticket sale end date and time |
| 452 |
* @param string $timezone Event timezone |
| 453 |
* |
| 454 |
* @return bool |
| 455 |
*/ |
| 456 |
function etn_is_ticket_sale_end( $end_date_time, $timezone = '' ) { |
| 457 |
$timezone = $timezone ?: wp_timezone_string(); |
| 458 |
try { |
| 459 |
$tz = new DateTimeZone( $timezone ); |
| 460 |
$event_end_dt = new DateTime( $end_date_time, $tz ); |
| 461 |
$current_dt = new DateTime( 'now', $tz ); |
| 462 |
} catch ( \Exception $e ) { |
| 463 |
// Unparseable inputs → treat as not-yet-ended rather than fataling. |
| 464 |
return false; |
| 465 |
} |
| 466 |
|
| 467 |
return $current_dt > $event_end_dt; |
| 468 |
} |
| 469 |
} |
| 470 |
|
| 471 |
if ( ! function_exists( 'etn_is_ticket_sale_start' ) ) { |
| 472 |
/** |
| 473 |
* Check an event has attendees or not |
| 474 |
* |
| 475 |
* @param string $start_date_time Event ticket sale start date and time |
| 476 |
* @param string $timezone Event timezone |
| 477 |
* |
| 478 |
* @return bool |
| 479 |
*/ |
| 480 |
function etn_is_ticket_sale_start( $start_date_time, $timezone = '' ) { |
| 481 |
$timezone = $timezone ?: wp_timezone_string(); |
| 482 |
try { |
| 483 |
$tz = new DateTimeZone( $timezone ); |
| 484 |
$event_date = new DateTime( $start_date_time, $tz ); |
| 485 |
$current_datte = new DateTime( 'now', $tz ); |
| 486 |
} catch ( \Exception $e ) { |
| 487 |
// Unparseable inputs → treat as already started rather than fataling. |
| 488 |
return true; |
| 489 |
} |
| 490 |
|
| 491 |
return $current_datte >= $event_date; |
| 492 |
} |
| 493 |
} |
| 494 |
|
| 495 |
|
| 496 |
if ( ! function_exists( 'etn_create_date_timezone' ) ) { |
| 497 |
/** |
| 498 |
* Create datetimezone object |
| 499 |
* |
| 500 |
* @param string $timezoneString Timezone |
| 501 |
* |
| 502 |
* @return string |
| 503 |
*/ |
| 504 |
function etn_create_date_timezone( $timezoneString ) { |
| 505 |
// List of valid named timezones |
| 506 |
$validTimezones = DateTimeZone::listIdentifiers(); |
| 507 |
|
| 508 |
// Check if the provided timezone is a valid named timezone |
| 509 |
if ( in_array( $timezoneString, $validTimezones ) ) { |
| 510 |
return $timezoneString; |
| 511 |
} |
| 512 |
|
| 513 |
// Check if the provided timezone is an offset timezone like UTC+6 or UTC-4.5 |
| 514 |
if ( preg_match('/^UTC([+-]\d{1,2})(?:\.(\d))?$/i', $timezoneString, $matches ) ) { |
| 515 |
// Convert the matched offset to a format recognized by DateTimeZone |
| 516 |
$hours = intval( $matches[1] ); |
| 517 |
$minutes = isset( $matches[2] ) ? intval($matches[2]) * 6 : 0; // 0.1 fractional part means 6 minutes |
| 518 |
|
| 519 |
// Ensure the format is like +06:30 or -04:30 |
| 520 |
$formattedOffset = sprintf( '%+03d:%02d', $hours, $minutes ); |
| 521 |
return $formattedOffset; |
| 522 |
} |
| 523 |
|
| 524 |
// If the timezone string doesn't match any known format, return default |
| 525 |
return 'America/New_York'; |
| 526 |
} |
| 527 |
} |
| 528 |
|
| 529 |
if ( ! function_exists( 'etn_convert_to_date' ) ) { |
| 530 |
/** |
| 531 |
* Convert to date from date time string |
| 532 |
* |
| 533 |
* @param string $datetimeString Datetime string |
| 534 |
* |
| 535 |
* @return string Date string |
| 536 |
*/ |
| 537 |
function etn_convert_to_date( $datetimeString ) { |
| 538 |
try { |
| 539 |
// Create a DateTime object using the provided datetime string |
| 540 |
$datetime = new DateTime( $datetimeString ); |
| 541 |
|
| 542 |
// Return the formatted date in 'Y-m-d' format |
| 543 |
return $datetime->format( 'Y-m-d' ); |
| 544 |
} catch ( Exception $e ) { |
| 545 |
return 'Error: ' . $e->getMessage(); |
| 546 |
} |
| 547 |
} |
| 548 |
} |
| 549 |
|
| 550 |
if ( ! function_exists( 'etn_get_currency' ) ) { |
| 551 |
/** |
| 552 |
* Get currency list |
| 553 |
* |
| 554 |
* @return array |
| 555 |
*/ |
| 556 |
function etn_get_currency () { |
| 557 |
$currencies = require Wpeventin::plugin_dir() . '/utils/currency.php'; |
| 558 |
|
| 559 |
return $currencies; |
| 560 |
} |
| 561 |
} |
| 562 |
|
| 563 |
if ( ! function_exists( 'etn_get_currency_by' ) ) { |
| 564 |
/** |
| 565 |
* Get currency by name, symbol |
| 566 |
* |
| 567 |
* @return string |
| 568 |
*/ |
| 569 |
function etn_get_currency_by_name( $name ) { |
| 570 |
$currencies = etn_get_currency(); |
| 571 |
|
| 572 |
foreach( $currencies as $currencie ) { |
| 573 |
if ( $currencie['name'] === $name ) { |
| 574 |
return $currencie; |
| 575 |
} |
| 576 |
} |
| 577 |
|
| 578 |
return null; |
| 579 |
} |
| 580 |
} |
| 581 |
|
| 582 |
if ( ! function_exists( 'etn_get_currency_symbol' ) ) { |
| 583 |
/** |
| 584 |
* Get currency by name, symbol |
| 585 |
* |
| 586 |
* @return string |
| 587 |
*/ |
| 588 |
function etn_get_currency_symbol( $name ) { |
| 589 |
$currency = etn_get_currency_by_name( $name ); |
| 590 |
|
| 591 |
if ( $currency ) { |
| 592 |
return $currency['symbol']; |
| 593 |
} |
| 594 |
} |
| 595 |
} |
| 596 |
|
| 597 |
if ( ! function_exists( 'etn_event_url_editable' ) ) { |
| 598 |
/** |
| 599 |
* Check editable url |
| 600 |
* |
| 601 |
* @return bool |
| 602 |
*/ |
| 603 |
function etn_event_url_editable () { |
| 604 |
$permalink_structure = get_option('permalink_structure'); |
| 605 |
|
| 606 |
if ( strpos( $permalink_structure, '%postname%' ) !== false) { |
| 607 |
return true; |
| 608 |
} |
| 609 |
|
| 610 |
return false; |
| 611 |
} |
| 612 |
} |
| 613 |
|
| 614 |
if ( ! function_exists( 'etn_get_timezone' ) ) { |
| 615 |
/** |
| 616 |
* Get valid timezonelists |
| 617 |
* |
| 618 |
* @return array Timezone lists |
| 619 |
*/ |
| 620 |
function etn_get_timezone() { |
| 621 |
$validTimezones = DateTimeZone::listIdentifiers(); |
| 622 |
|
| 623 |
return $validTimezones; |
| 624 |
} |
| 625 |
} |
| 626 |
|
| 627 |
if ( ! function_exists( 'etn_get_wp_timezones' ) ) { |
| 628 |
/** |
| 629 |
* Get valid wp timezone |
| 630 |
* |
| 631 |
* @return string |
| 632 |
*/ |
| 633 |
function etn_get_wp_timezones() { |
| 634 |
$timezones = []; |
| 635 |
|
| 636 |
// Generate UTC offsets |
| 637 |
$offset_range = range( -12, 14 ); |
| 638 |
foreach ( $offset_range as $offset ) { |
| 639 |
// Whole hour |
| 640 |
$hours = ( $offset > 0 ) ? '+' . $offset : (string) $offset; |
| 641 |
|
| 642 |
if ( $offset != 0 ) { |
| 643 |
$timezones[] = 'UTC' . $hours; |
| 644 |
} |
| 645 |
else{ |
| 646 |
$timezones[] = 'UTC' . '+'.$hours; |
| 647 |
} |
| 648 |
|
| 649 |
// Half-hour offsets |
| 650 |
if ( $offset != 0 ) { |
| 651 |
$timezones[] = 'UTC' . $hours . ':30'; |
| 652 |
} |
| 653 |
|
| 654 |
if ( $offset == 0 ) { |
| 655 |
$timezones[] = 'UTC' . '+'.$hours . ':30'; |
| 656 |
$timezones[] = 'UTC' . '-'.$hours . ':30'; |
| 657 |
} |
| 658 |
|
| 659 |
// Special quarter-hour offsets |
| 660 |
if ( in_array( $offset, [ 5, 8, 12, 13 ] ) ) { |
| 661 |
$timezones[] = 'UTC+' . $offset . ':45'; |
| 662 |
} |
| 663 |
} |
| 664 |
|
| 665 |
// Now add all region identifiers |
| 666 |
$timezones = array_merge( $timezones, DateTimeZone::listIdentifiers() ); |
| 667 |
|
| 668 |
return $timezones; |
| 669 |
} |
| 670 |
} |
| 671 |
|
| 672 |
if ( ! function_exists( 'etn_prepare_address' ) ) { |
| 673 |
/** |
| 674 |
* Prepare event address from event location |
| 675 |
* |
| 676 |
* This function is written for temporary solution. We have a nested location issue @since 4.0.0. To resolve this we impletent a temporary function. We have to remove this function when v4.0 is completely statble from location issue. Before remove this function make sure remove from all of the place where we used this. |
| 677 |
* |
| 678 |
* @param array $location |
| 679 |
* |
| 680 |
* @return string |
| 681 |
*/ |
| 682 |
function etn_prepare_address( $location ) { |
| 683 |
static $depth = 0; |
| 684 |
|
| 685 |
if ( $depth >= 10 ) { |
| 686 |
return ''; |
| 687 |
} |
| 688 |
|
| 689 |
$depth++; |
| 690 |
|
| 691 |
if ( ! is_array( $location ) ) { |
| 692 |
return $location; |
| 693 |
} |
| 694 |
|
| 695 |
$address = ! empty( $location['address'] ) ? $location['address'] : ''; |
| 696 |
|
| 697 |
return etn_prepare_address( $address ); |
| 698 |
} |
| 699 |
} |
| 700 |
|
| 701 |
if ( ! function_exists( 'etn_get_email_settings' ) ) { |
| 702 |
|
| 703 |
/** |
| 704 |
* Get email settings |
| 705 |
* |
| 706 |
* @param string $email Email name for the email setting |
| 707 |
* |
| 708 |
* @return array |
| 709 |
*/ |
| 710 |
function etn_get_email_settings( $email = '' ) { |
| 711 |
|
| 712 |
$email_settings = etn_get_option( 'email' ); |
| 713 |
|
| 714 |
$defaults = etn_get_default_email_settings(); |
| 715 |
|
| 716 |
$email_settings = etn_recursive_wp_parse_args( $email_settings, $defaults ); |
| 717 |
|
| 718 |
if ( ! $email ) { |
| 719 |
return $email_settings; |
| 720 |
} |
| 721 |
|
| 722 |
return $email_settings[$email]; |
| 723 |
} |
| 724 |
} |
| 725 |
|
| 726 |
if ( ! function_exists( 'etn_get_default_email_settings' ) ) { |
| 727 |
/** |
| 728 |
* Get default email settings for the email template |
| 729 |
* |
| 730 |
* @return array |
| 731 |
*/ |
| 732 |
function etn_get_default_email_settings() { |
| 733 |
|
| 734 |
$email_settings = [ |
| 735 |
'purchase_email' => [ |
| 736 |
'from' => get_option( 'admin_email' ), |
| 737 |
'subject' => sprintf( __( 'Event Ticket', 'eventin' ) ), |
| 738 |
'body' => __( 'You have purchased ticket(s). Attendee ticket details are as follows.', 'eventin' ), |
| 739 |
'send_to_admin' => true, |
| 740 |
'send_email_to_attendees' => true |
| 741 |
], |
| 742 |
'certificate_email' => [ |
| 743 |
'from' => get_option( 'admin_email' ), |
| 744 |
'subject' => sprintf( __( 'Event Certificate', 'eventin' ) ), |
| 745 |
// translators: %1$s is the event title |
| 746 |
'body' => sprintf( __( '<p>Congratulations for successfully attending/completing the event \'%1$s\'. Your certificate is ready! Click on the link provided below to get the PDF certificate. </p>', 'eventin' ), '<span>{%event_title%}</span>' ), // phpcs:ignore WordPress.WP.I18n.NoHtmlWrappedStrings |
| 747 |
'send_to_admin' => true, |
| 748 |
], |
| 749 |
'rsv_email' => [ |
| 750 |
'from' => get_option( 'admin_email' ), |
| 751 |
'response_type' => 'going', |
| 752 |
'subject' => sprintf( __( 'RSVP request', 'eventin' ) ), |
| 753 |
'body' => sprintf( __( 'We received your RSVP request', 'eventin' ) ), |
| 754 |
'send_to_admin' => true, |
| 755 |
], |
| 756 |
'reminder_email' => [ |
| 757 |
'from' => get_option( 'admin_email' ), |
| 758 |
'subject' => sprintf( __( 'Reminder email', 'eventin' ) ), |
| 759 |
'body' => __( 'Just sending you a quick reminder about our retailer meet-up you\'ve registered to attend in two days time. If you\'ve misplaced the Invitation that contained all the details. don\'t worry. Cve added them rn below for you.', 'eventin' ), |
| 760 |
'send_to_admin' => true, |
| 761 |
] |
| 762 |
]; |
| 763 |
|
| 764 |
return apply_filters( 'etn_default_email_settings', $email_settings ); |
| 765 |
} |
| 766 |
} |
| 767 |
|
| 768 |
if ( ! function_exists( 'etn_recursive_wp_parse_args' ) ) { |
| 769 |
/** |
| 770 |
* Perse args recursively |
| 771 |
* |
| 772 |
* @param array $args [$args description] |
| 773 |
* @param array $defaults [$defaults description] |
| 774 |
* |
| 775 |
* @return array [return description] |
| 776 |
*/ |
| 777 |
function etn_recursive_wp_parse_args( $args, $defaults ) { |
| 778 |
$args = (array) $args; // Ensure args is an array |
| 779 |
|
| 780 |
// Loop through each default value and apply wp_parse_args recursively if it's an array |
| 781 |
foreach ( $defaults as $key => $value ) { |
| 782 |
if ( is_array( $value ) ) { |
| 783 |
// If the key is an array, call recursively |
| 784 |
$args[$key] = etn_recursive_wp_parse_args( isset( $args[$key] ) ? $args[$key] : [], $value ); |
| 785 |
} else { |
| 786 |
// Otherwise, use wp_parse_args for the non-array values |
| 787 |
if ( ! isset( $args[$key] ) ) { |
| 788 |
$args[$key] = $value; |
| 789 |
} |
| 790 |
} |
| 791 |
} |
| 792 |
|
| 793 |
return $args; |
| 794 |
} |
| 795 |
} |
| 796 |
|
| 797 |
if ( !function_exists( 'etn_editor_settings' ) ) { |
| 798 |
/** |
| 799 |
* Retrieves the settings for the Gutenberg editor. |
| 800 |
* |
| 801 |
* This function retrieves the settings for the Gutenberg editor, including the allowed block types, |
| 802 |
* typography, color palette, and other experimental features. It also applies filters to allow |
| 803 |
* customization of the settings. |
| 804 |
* |
| 805 |
* @return array The settings for the Gutenberg editor. |
| 806 |
*/ |
| 807 |
function etn_editor_settings() { |
| 808 |
|
| 809 |
|
| 810 |
$coreSettings = get_block_editor_settings( [], 'post' ); |
| 811 |
$wordpressCoreTypography = $coreSettings['__experimentalFeatures']['typography']; |
| 812 |
$coreExperimentalSpacing = $coreSettings['__experimentalFeatures']['spacing']; |
| 813 |
|
| 814 |
|
| 815 |
$themePref = getThemePrefScheme(); |
| 816 |
|
| 817 |
$settings = array( |
| 818 |
'gradients' => [], |
| 819 |
'alignWide' => false, |
| 820 |
'allowedMimeTypes' => get_allowed_mime_types(), |
| 821 |
'__experimentalBlockPatterns' => [], |
| 822 |
'__experimentalFeatures' => [ |
| 823 |
'appearanceTools' => true, |
| 824 |
'border' => [ |
| 825 |
'color' => false, |
| 826 |
'radius' => true, |
| 827 |
'style' => false, |
| 828 |
'width' => false, |
| 829 |
], |
| 830 |
'color' => [ |
| 831 |
'background' => true, |
| 832 |
'customDuotone' => false, |
| 833 |
'defaultGradients' => false, |
| 834 |
'defaultPalette' => false, |
| 835 |
'duotone' => [], |
| 836 |
'gradients' => [], |
| 837 |
'link' => false, |
| 838 |
'palette' => [ |
| 839 |
'theme' => $themePref['colors'], |
| 840 |
], |
| 841 |
'text' => true, |
| 842 |
], |
| 843 |
'spacing' => $coreExperimentalSpacing, |
| 844 |
'typography' => $wordpressCoreTypography, |
| 845 |
'blocks' => [ |
| 846 |
'core/button' => [ |
| 847 |
'border' => [ |
| 848 |
'radius' => true, |
| 849 |
"style" => true, |
| 850 |
"width" => true, |
| 851 |
], |
| 852 |
'typography' => [ |
| 853 |
'fontSizes' => [], |
| 854 |
], |
| 855 |
'spacing' => $coreExperimentalSpacing, |
| 856 |
], |
| 857 |
|
| 858 |
], |
| 859 |
], |
| 860 |
'__experimentalSetIsInserterOpened' => false, |
| 861 |
'disableCustomColors' => get_theme_support( 'disable-custom-colors' ), |
| 862 |
'disableCustomFontSizes' => false, |
| 863 |
'disableCustomGradients' => true, |
| 864 |
'enableCustomLineHeight' => get_theme_support( 'custom-line-height' ), |
| 865 |
'enableCustomSpacing' => get_theme_support( 'custom-spacing' ), |
| 866 |
'enableCustomUnits' => false, |
| 867 |
'keepCaretInsideBlock' => false, |
| 868 |
'mediaLibrary' => ['type' => true, 'date' => true, 'allowedTypes' => ['image', 'video', 'audio', 'application']], |
| 869 |
'mediaUpload' => true, |
| 870 |
); |
| 871 |
|
| 872 |
$color_palette = current( (array) get_theme_support( 'editor-color-palette' ) ); |
| 873 |
if ( false !== $color_palette ) { |
| 874 |
$settings['colors'] = $color_palette; |
| 875 |
} else { |
| 876 |
$settings['colors'] = []; |
| 877 |
} |
| 878 |
|
| 879 |
return $settings; |
| 880 |
} |
| 881 |
} |
| 882 |
|
| 883 |
if ( !function_exists( 'getThemePrefScheme' ) ) { |
| 884 |
/** |
| 885 |
* Retrieves the theme preference scheme. |
| 886 |
* |
| 887 |
* This function retrieves the theme preference scheme, which includes the color palette and font sizes. |
| 888 |
* The color palette is an array of objects, each representing a color with its name, slug, and hex code. |
| 889 |
* The font sizes is an array of objects, each representing a font size with its name, short name, size, and slug. |
| 890 |
* The function applies filters to allow customization of the theme preference scheme. |
| 891 |
* |
| 892 |
* @return array The theme preference scheme. |
| 893 |
*/ |
| 894 |
function getThemePrefScheme() { |
| 895 |
static $pref; |
| 896 |
if ( !$pref ) { |
| 897 |
|
| 898 |
$color_palette = [ |
| 899 |
[ |
| 900 |
"name" => __( "Black", "eventin" ), |
| 901 |
"slug" => "black", |
| 902 |
"color" => "#000000", |
| 903 |
], |
| 904 |
[ |
| 905 |
"name" => __( "Cyan bluish gray", "eventin" ), |
| 906 |
"slug" => "cyan-bluish-gray", |
| 907 |
"color" => "#abb8c3", |
| 908 |
], |
| 909 |
[ |
| 910 |
"name" => __( "White", "eventin" ), |
| 911 |
"slug" => "white", |
| 912 |
"color" => "#ffffff", |
| 913 |
], |
| 914 |
[ |
| 915 |
"name" => __( "Pale pink", "eventin" ), |
| 916 |
"slug" => "pale-pink", |
| 917 |
"color" => "#f78da7", |
| 918 |
], |
| 919 |
[ |
| 920 |
"name" => __( "Luminous vivid orange", "eventin" ), |
| 921 |
"slug" => "luminous-vivid-orange", |
| 922 |
"color" => "#ff6900", |
| 923 |
], |
| 924 |
[ |
| 925 |
"name" => __( "Luminous vivid amber", "eventin" ), |
| 926 |
"slug" => "luminous-vivid-amber", |
| 927 |
"color" => "#fcb900", |
| 928 |
], |
| 929 |
[ |
| 930 |
"name" => __( "Light green cyan", "eventin" ), |
| 931 |
"slug" => "light-green-cyan", |
| 932 |
"color" => "#7bdcb5", |
| 933 |
], |
| 934 |
[ |
| 935 |
"name" => __( "Vivid green cyan", "eventin" ), |
| 936 |
"slug" => "vivid-green-cyan", |
| 937 |
"color" => "#00d084", |
| 938 |
], |
| 939 |
[ |
| 940 |
"name" => __( "Pale cyan blue", "eventin" ), |
| 941 |
"slug" => "pale-cyan-blue", |
| 942 |
"color" => "#8ed1fc", |
| 943 |
], |
| 944 |
[ |
| 945 |
"name" => __( "Vivid cyan blue", "eventin" ), |
| 946 |
"slug" => "vivid-cyan-blue", |
| 947 |
"color" => "#0693e3", |
| 948 |
], |
| 949 |
[ |
| 950 |
"name" => __( "Vivid purple", "eventin" ), |
| 951 |
"slug" => "vivid-purple", |
| 952 |
"color" => "#9b51e0", |
| 953 |
], |
| 954 |
]; |
| 955 |
|
| 956 |
$font_sizes = [ |
| 957 |
[ |
| 958 |
'name' => __( 'Small', 'eventin' ), |
| 959 |
'shortName' => 'S', |
| 960 |
'size' => 14, |
| 961 |
'slug' => 'small', |
| 962 |
], |
| 963 |
[ |
| 964 |
'name' => __( 'Medium', 'eventin' ), |
| 965 |
'shortName' => 'M', |
| 966 |
'size' => 18, |
| 967 |
'slug' => 'medium', |
| 968 |
], |
| 969 |
[ |
| 970 |
'name' => __( 'Large', 'eventin' ), |
| 971 |
'shortName' => 'L', |
| 972 |
'size' => 24, |
| 973 |
'slug' => 'large', |
| 974 |
], |
| 975 |
[ |
| 976 |
'name' => __( 'Larger', 'eventin' ), |
| 977 |
'shortName' => 'XL', |
| 978 |
'size' => 32, |
| 979 |
'slug' => 'larger', |
| 980 |
], |
| 981 |
]; |
| 982 |
|
| 983 |
$pref = apply_filters( 'eventin/theme_pref', [ |
| 984 |
'colors' => (array) $color_palette, |
| 985 |
'font_sizes' => (array) $font_sizes, |
| 986 |
] ); |
| 987 |
} |
| 988 |
|
| 989 |
return $pref; |
| 990 |
|
| 991 |
} |
| 992 |
} |
| 993 |
|
| 994 |
if ( ! function_exists( 'etn_currency' ) ) { |
| 995 |
/** |
| 996 |
* Get currecny |
| 997 |
* |
| 998 |
* @return string |
| 999 |
*/ |
| 1000 |
function etn_currency() { |
| 1001 |
$payment_method = etn_get_option( 'payment_method' ); |
| 1002 |
|
| 1003 |
$is_enabled_wc = 'woocommerce' === $payment_method; |
| 1004 |
|
| 1005 |
$is_enabled_sc = etn_get_option('surecart_status'); |
| 1006 |
|
| 1007 |
$is_enabled_fc = etn_get_option('fluentcart_status'); |
| 1008 |
|
| 1009 |
if ( $is_enabled_sc && class_exists(SureCart::class) ) { |
| 1010 |
return \SureCart::account()->currency; |
| 1011 |
} |
| 1012 |
|
| 1013 |
if ( $is_enabled_fc && defined('FLUENTCART_VERSION') ) { |
| 1014 |
return (new \FluentCart\Api\StoreSettings())->getCurrency(); |
| 1015 |
} |
| 1016 |
|
| 1017 |
if ( function_exists('WC') && $is_enabled_wc ) { |
| 1018 |
return get_woocommerce_currency(); |
| 1019 |
} |
| 1020 |
|
| 1021 |
$currency = etn_get_option( 'etn_settings_country_currency', 'USD' ); |
| 1022 |
|
| 1023 |
return $currency; |
| 1024 |
} |
| 1025 |
} |
| 1026 |
|
| 1027 |
if ( ! function_exists( 'etn_currency_symbol' ) ) { |
| 1028 |
/** |
| 1029 |
* Get currency symbol |
| 1030 |
* |
| 1031 |
* @return string |
| 1032 |
*/ |
| 1033 |
function etn_currency_symbol() { |
| 1034 |
$currency = etn_currency(); |
| 1035 |
|
| 1036 |
$is_enabled_sc = etn_get_option('surecart_status'); |
| 1037 |
|
| 1038 |
$is_enabled_fc = etn_get_option('fluentcart_status'); |
| 1039 |
|
| 1040 |
if ( $is_enabled_sc && class_exists(SureCart::class) ) { |
| 1041 |
return html_entity_decode( Currency::getCurrencySymbol( \SureCart::account()->currency ) ); |
| 1042 |
} |
| 1043 |
|
| 1044 |
if ( $is_enabled_fc && defined('FLUENTCART_VERSION') ) { |
| 1045 |
return (new \FluentCart\Api\StoreSettings())->getCurrencySymbol(); |
| 1046 |
} |
| 1047 |
|
| 1048 |
return etn_get_currency_symbol( $currency ); |
| 1049 |
} |
| 1050 |
} |
| 1051 |
|
| 1052 |
if ( ! function_exists( 'etn_is_enable_wc' ) ) { |
| 1053 |
/** |
| 1054 |
* Check event in is used woocommerce payment method |
| 1055 |
* |
| 1056 |
* @return bool |
| 1057 |
*/ |
| 1058 |
function etn_is_enable_wc() { |
| 1059 |
$payment_method = etn_get_option( 'payment_method' ); |
| 1060 |
|
| 1061 |
return function_exists( 'WC' ) && 'woocommerce' === $payment_method; |
| 1062 |
} |
| 1063 |
} |
| 1064 |
|
| 1065 |
if ( ! function_exists( 'etn_get_thousand_separator' ) ) { |
| 1066 |
|
| 1067 |
/** |
| 1068 |
* Thousand separator |
| 1069 |
* |
| 1070 |
* @return string |
| 1071 |
*/ |
| 1072 |
function etn_get_thousand_separator() { |
| 1073 |
if ( etn_is_enable_wc() ) { |
| 1074 |
return wc_get_price_thousand_separator(); |
| 1075 |
} |
| 1076 |
|
| 1077 |
$thousand_separator = etn_get_option( 'thousand_separator', ',' ); |
| 1078 |
|
| 1079 |
return apply_filters( 'etn_thousand_separator', $thousand_separator ); |
| 1080 |
} |
| 1081 |
} |
| 1082 |
|
| 1083 |
if ( ! function_exists( 'etn_get_decimal_separator' ) ) { |
| 1084 |
|
| 1085 |
/** |
| 1086 |
* Get descimal separator |
| 1087 |
* |
| 1088 |
* @return string |
| 1089 |
*/ |
| 1090 |
function etn_get_decimal_separator() { |
| 1091 |
if ( etn_is_enable_wc() ) { |
| 1092 |
return wc_get_price_decimal_separator(); |
| 1093 |
} |
| 1094 |
|
| 1095 |
$decimal_separator = etn_get_option( 'decimal_separator', 'comma_dot' ); |
| 1096 |
|
| 1097 |
return apply_filters( 'etn_decimal_separator', $decimal_separator ); |
| 1098 |
} |
| 1099 |
} |
| 1100 |
|
| 1101 |
if ( ! function_exists( 'etn_get_decimals' ) ) { |
| 1102 |
|
| 1103 |
/** |
| 1104 |
* Get number of decimals |
| 1105 |
* |
| 1106 |
* @return string |
| 1107 |
*/ |
| 1108 |
function etn_get_decimals() { |
| 1109 |
if ( etn_is_enable_wc() ) { |
| 1110 |
return wc_get_price_decimals(); |
| 1111 |
} |
| 1112 |
|
| 1113 |
$decimals = etn_get_option( 'decimals', 0 ); |
| 1114 |
|
| 1115 |
return apply_filters( 'etn_decimals', $decimals ); |
| 1116 |
} |
| 1117 |
} |
| 1118 |
|
| 1119 |
if ( ! function_exists( 'etn_get_price_format' ) ) { |
| 1120 |
|
| 1121 |
/** |
| 1122 |
* Get price format |
| 1123 |
* |
| 1124 |
* @return string |
| 1125 |
*/ |
| 1126 |
function etn_get_price_format() { |
| 1127 |
if ( etn_is_enable_wc() ) { |
| 1128 |
return get_woocommerce_price_format(); |
| 1129 |
} |
| 1130 |
|
| 1131 |
$currency_pos = get_option( 'currency_position' ); |
| 1132 |
$format = '%1$s%2$s'; |
| 1133 |
|
| 1134 |
switch ( $currency_pos ) { |
| 1135 |
case 'left': |
| 1136 |
$format = '%1$s%2$s'; |
| 1137 |
break; |
| 1138 |
case 'right': |
| 1139 |
$format = '%2$s%1$s'; |
| 1140 |
break; |
| 1141 |
case 'left_space': |
| 1142 |
$format = '%1$s %2$s'; |
| 1143 |
break; |
| 1144 |
case 'right_space': |
| 1145 |
$format = '%2$s %1$s'; |
| 1146 |
break; |
| 1147 |
} |
| 1148 |
|
| 1149 |
return apply_filters( 'etn_price_format', $format, $currency_pos ); |
| 1150 |
} |
| 1151 |
} |
| 1152 |
|
| 1153 |
if ( ! function_exists( 'etn_get_currency_position' ) ) { |
| 1154 |
|
| 1155 |
/** |
| 1156 |
* Get price format |
| 1157 |
* |
| 1158 |
* @return string |
| 1159 |
*/ |
| 1160 |
function etn_get_currency_position() { |
| 1161 |
if ( etn_is_enable_wc() ) { |
| 1162 |
$currency_pos = get_option( 'woocommerce_currency_pos' ); |
| 1163 |
|
| 1164 |
return $currency_pos; |
| 1165 |
} |
| 1166 |
|
| 1167 |
$currency_pos = etn_get_option( 'currency_position', 'left' ); |
| 1168 |
|
| 1169 |
return apply_filters( 'etn_currency_position', $currency_pos ); |
| 1170 |
} |
| 1171 |
} |
| 1172 |
|
| 1173 |
if ( ! function_exists( 'etn_get_wc_order_status_list' ) ) { |
| 1174 |
/** |
| 1175 |
* Get all woocommerce order statuses without prefix |
| 1176 |
* |
| 1177 |
* @return array Woocommerce order statuses |
| 1178 |
*/ |
| 1179 |
function etn_get_wc_order_status_list() { |
| 1180 |
$statuses_without_prefix = []; |
| 1181 |
|
| 1182 |
if ( ! function_exists( 'WC' ) ) { |
| 1183 |
return $statuses_without_prefix; |
| 1184 |
} |
| 1185 |
|
| 1186 |
$statuses = wc_get_order_statuses(); |
| 1187 |
|
| 1188 |
|
| 1189 |
foreach ( $statuses as $key => $label ) { |
| 1190 |
// Remove the 'wc-' prefix from each status key. |
| 1191 |
$new_key = str_replace( 'wc-', '', $key ); |
| 1192 |
$statuses_without_prefix[$new_key] = $label; |
| 1193 |
} |
| 1194 |
|
| 1195 |
return $statuses_without_prefix; |
| 1196 |
} |
| 1197 |
} |
| 1198 |
|
| 1199 |
if ( ! function_exists( 'etn_get_wc_order_statuses' ) ) { |
| 1200 |
/** |
| 1201 |
* Get all woocommerce order statuses stored as eventin settings |
| 1202 |
* |
| 1203 |
* @return array Woocommerce order statuses |
| 1204 |
*/ |
| 1205 |
function etn_get_wc_order_statuses() { |
| 1206 |
$defaults = [ 'completed', 'processing' ]; |
| 1207 |
$settings = etn_get_option( 'wc_order_statuses', $defaults ); |
| 1208 |
|
| 1209 |
return $settings; |
| 1210 |
} |
| 1211 |
} |
| 1212 |
|
| 1213 |
if ( ! function_exists( 'etn_is_enable_wc_synchronize_order' ) ) { |
| 1214 |
/** |
| 1215 |
* Check wc order synchronizes orders to the posts table enable or not |
| 1216 |
* |
| 1217 |
* @return bool Retur true if synchronizes orders is enable otherwise false |
| 1218 |
*/ |
| 1219 |
function etn_is_enable_wc_synchronize_order() { |
| 1220 |
|
| 1221 |
if ( false === get_option( 'woocommerce_custom_orders_table_enabled' ) ) { |
| 1222 |
return true; |
| 1223 |
} |
| 1224 |
|
| 1225 |
$enable_custom_order_table = get_option( 'woocommerce_custom_orders_table_enabled', true ); |
| 1226 |
|
| 1227 |
if ( 'no' === $enable_custom_order_table ) { |
| 1228 |
return true; |
| 1229 |
} |
| 1230 |
|
| 1231 |
$data_sync_enabled = get_option( 'woocommerce_custom_orders_table_data_sync_enabled', true ); |
| 1232 |
|
| 1233 |
if ( 'yes' === $data_sync_enabled ) { |
| 1234 |
return true; |
| 1235 |
} |
| 1236 |
|
| 1237 |
return false; |
| 1238 |
} |
| 1239 |
} |
| 1240 |
|
| 1241 |
if ( ! function_exists( 'etn_gutenberg_template_path' ) ) { |
| 1242 |
/** |
| 1243 |
* Gutenberg block paths |
| 1244 |
* |
| 1245 |
* @return string |
| 1246 |
*/ |
| 1247 |
function etn_block_path() { |
| 1248 |
return Wpeventin::core_dir() . 'Blocks/'; |
| 1249 |
} |
| 1250 |
} |
| 1251 |
|
| 1252 |
if ( ! function_exists( 'etn_upload_image_from_url' ) ) { |
| 1253 |
/** |
| 1254 |
* Upload image from url |
| 1255 |
* |
| 1256 |
* @param string $image_url Public Image url |
| 1257 |
* |
| 1258 |
* @return integer Image attatchment id |
| 1259 |
*/ |
| 1260 |
function etn_upload_image_from_url( $image_url ) { |
| 1261 |
// Check if the URL is valid |
| 1262 |
if ( empty( $image_url ) || ! filter_var( $image_url, FILTER_VALIDATE_URL ) ) { |
| 1263 |
return new WP_Error('invalid_url', __('Invalid image URL.', 'eventin')); |
| 1264 |
} |
| 1265 |
|
| 1266 |
// Get the file name from the URL |
| 1267 |
$file_name = basename( wp_parse_url( $image_url, PHP_URL_PATH ) ); |
| 1268 |
|
| 1269 |
// Download the image |
| 1270 |
$image_data = wp_remote_get( $image_url, [ |
| 1271 |
'timeout' => 30, // Increase timeout to 30 seconds |
| 1272 |
'blocking' => true, |
| 1273 |
] ); |
| 1274 |
if ( is_wp_error( $image_data ) ) { |
| 1275 |
return new WP_Error( 'download_failed', __( 'Failed to download image.', 'eventin') ); |
| 1276 |
} |
| 1277 |
|
| 1278 |
$image_body = wp_remote_retrieve_body( $image_data ); |
| 1279 |
if ( empty( $image_body ) ) { |
| 1280 |
return new WP_Error( 'empty_image', __( 'Downloaded image is empty.', 'eventin' ) ); |
| 1281 |
} |
| 1282 |
|
| 1283 |
// Get the upload directory |
| 1284 |
$upload_dir = wp_upload_dir(); |
| 1285 |
$file_path = $upload_dir['path'] . '/' . $file_name; |
| 1286 |
|
| 1287 |
// Save the image file |
| 1288 |
if ( ! file_put_contents( $file_path, $image_body ) ) { |
| 1289 |
return new WP_Error( 'file_write_error', __('Failed to write image file.', 'eventin' ) ); |
| 1290 |
} |
| 1291 |
|
| 1292 |
// Prepare file data for WordPress upload |
| 1293 |
$file_info = [ |
| 1294 |
'name' => $file_name, |
| 1295 |
'type' => mime_content_type( $file_path ), |
| 1296 |
'tmp_name' => $file_path, |
| 1297 |
'error' => 0, |
| 1298 |
'size' => filesize( $file_path ), |
| 1299 |
]; |
| 1300 |
|
| 1301 |
// Include WordPress file handling functions |
| 1302 |
require_once ABSPATH . 'wp-admin/includes/image.php'; |
| 1303 |
require_once ABSPATH . 'wp-admin/includes/file.php'; |
| 1304 |
require_once ABSPATH . 'wp-admin/includes/media.php'; |
| 1305 |
|
| 1306 |
// Upload image to WordPress media library |
| 1307 |
$attachment_id = media_handle_sideload($file_info, 0); |
| 1308 |
|
| 1309 |
// Check for errors |
| 1310 |
if ( is_wp_error( $attachment_id ) ) { |
| 1311 |
wp_delete_file($file_path); // Delete the temporary file if upload fails |
| 1312 |
return $attachment_id; |
| 1313 |
} |
| 1314 |
|
| 1315 |
// Delete the temporary file after successful upload |
| 1316 |
wp_delete_file($file_path); |
| 1317 |
|
| 1318 |
return $attachment_id; // Return the attachment ID |
| 1319 |
} |
| 1320 |
} |
| 1321 |
|
| 1322 |
if ( ! function_exists( 'etn_date_format' ) ) { |
| 1323 |
/** |
| 1324 |
* Get date formate from wordpress |
| 1325 |
* |
| 1326 |
* @return string |
| 1327 |
*/ |
| 1328 |
function etn_date_format() { |
| 1329 |
$date_format = get_option( 'date_format' ); |
| 1330 |
|
| 1331 |
return $date_format; |
| 1332 |
} |
| 1333 |
} |
| 1334 |
|
| 1335 |
if ( ! function_exists( 'etn_time_format' ) ) { |
| 1336 |
/** |
| 1337 |
* Get time formate from wordpress |
| 1338 |
* |
| 1339 |
* @return string |
| 1340 |
*/ |
| 1341 |
function etn_time_format() { |
| 1342 |
$time_format = get_option( 'time_format' ); |
| 1343 |
|
| 1344 |
return $time_format; |
| 1345 |
} |
| 1346 |
} |
| 1347 |
|
| 1348 |
if ( ! function_exists( 'etn_validate_event_tickets' ) ) { |
| 1349 |
/** |
| 1350 |
* Validate event tickets that verify to prevent overselling |
| 1351 |
* |
| 1352 |
* @param integer $event_id Event id that will be purchased |
| 1353 |
* @param array $order_tickets Order ticket list |
| 1354 |
* @param bool $is_for_update Whether this is an update operation |
| 1355 |
* @param bool $is_waiting Whether this is for waiting list |
| 1356 |
* |
| 1357 |
* @return bool | WP_Error |
| 1358 |
*/ |
| 1359 |
function etn_validate_event_tickets( $event_id, $order_tickets, $is_for_update = false, $is_waiting = false ) { |
| 1360 |
$event = new Event_Model( $event_id ); |
| 1361 |
$sold_tickets = (array)Helper::etn_get_sold_tickets_by_event( $event_id ); |
| 1362 |
|
| 1363 |
$enable_global_stock = get_post_meta( $event_id, 'etn_enable_global_stock', true ); |
| 1364 |
$global_stock = intval( get_post_meta( $event_id, 'etn_global_stock', true ) ); |
| 1365 |
|
| 1366 |
if ( $enable_global_stock && $global_stock > 0 ) { |
| 1367 |
$total_requested = 0; |
| 1368 |
foreach ( $order_tickets as $ticket ) { |
| 1369 |
$total_requested += intval( $ticket['ticket_quantity'] ); |
| 1370 |
} |
| 1371 |
|
| 1372 |
$total_sold = array_sum( $sold_tickets ); |
| 1373 |
|
| 1374 |
$ticket_variations = $event->etn_ticket_variations; |
| 1375 |
$total_pending = 0; |
| 1376 |
if ( is_array( $ticket_variations ) ) { |
| 1377 |
foreach ( $ticket_variations as $ticket_var ) { |
| 1378 |
$total_pending += intval( $ticket_var['pending'] ?? 0 ); |
| 1379 |
} |
| 1380 |
} |
| 1381 |
|
| 1382 |
$remaining = $global_stock - $total_sold - $total_pending; |
| 1383 |
if ( $is_for_update ) { |
| 1384 |
$remaining += $total_requested; |
| 1385 |
} |
| 1386 |
|
| 1387 |
if ( $is_waiting ) { |
| 1388 |
$global_waiting_list = intval( get_post_meta( $event_id, 'etn_global_waiting_list', true ) ); |
| 1389 |
$total_waiting = array_sum( etn_get_waiting_list_counts_by_slug( $event_id ) ); |
| 1390 |
$waiting_remaining = $global_waiting_list - $total_waiting; |
| 1391 |
if ( $total_requested > $waiting_remaining ) { |
| 1392 |
return new WP_Error( |
| 1393 |
'waiting_list_limit', |
| 1394 |
// translators: %d is the number of waiting-list spots still available. |
| 1395 |
sprintf( __( 'Waiting list is full. Only %d spots available.', 'eventin' ), max( 0, $waiting_remaining ) ), |
| 1396 |
['status' => 422] |
| 1397 |
); |
| 1398 |
} |
| 1399 |
} else { |
| 1400 |
if ( $total_requested > $remaining ) { |
| 1401 |
$only_left = max( 0, $remaining ); |
| 1402 |
return new WP_Error( |
| 1403 |
'global_stock_limit', |
| 1404 |
// translators: %d is the number of tickets still left for the event. |
| 1405 |
sprintf( __( 'Only %d tickets left for this event.', 'eventin' ), $only_left ), |
| 1406 |
['status' => 422] |
| 1407 |
); |
| 1408 |
} |
| 1409 |
} |
| 1410 |
|
| 1411 |
return true; |
| 1412 |
} |
| 1413 |
|
| 1414 |
foreach( $order_tickets as $ticket ) { |
| 1415 |
$event_ticket = $event->get_ticket( $ticket['ticket_slug'] ); |
| 1416 |
|
| 1417 |
if ( ! $event_ticket ) { |
| 1418 |
return new WP_Error( 'ticket_not_found', __( 'Ticket not found', 'eventin' ), ['status' => 422] ); |
| 1419 |
} |
| 1420 |
|
| 1421 |
$available = $event_ticket['etn_avaiilable_tickets'] ?? 0; |
| 1422 |
$sold = $sold_tickets[$ticket['ticket_slug']] ?? 0; |
| 1423 |
$pending = $event_ticket['pending'] ?? 0; |
| 1424 |
|
| 1425 |
if ( ! isset( $available ) || ! is_numeric( $available ) ) { |
| 1426 |
return true; |
| 1427 |
} |
| 1428 |
|
| 1429 |
$ticket_left = intval( $available ) - intval( $sold ) - intval( $pending ); |
| 1430 |
if ( $is_for_update ) { |
| 1431 |
$ticket_left += intval( $ticket['ticket_quantity'] ); |
| 1432 |
} |
| 1433 |
|
| 1434 |
if ( $is_waiting ) { |
| 1435 |
$waiting_counts = etn_get_waiting_list_counts_by_slug( $event_id ); |
| 1436 |
$ticket_waiting_limit = intval( $event_ticket['etn_ticket_waiting_list_limit'] ?? 0 ); |
| 1437 |
$waiting_for_slug = $waiting_counts[ $ticket['ticket_slug'] ] ?? 0; |
| 1438 |
$waiting_remaining = $ticket_waiting_limit - $waiting_for_slug; |
| 1439 |
if ( intval( $ticket['ticket_quantity'] ) > $waiting_remaining ) { |
| 1440 |
return new WP_Error( |
| 1441 |
'waiting_list_limit', |
| 1442 |
// translators: %d is the number of waiting-list spots still available for this ticket type. |
| 1443 |
sprintf( __( 'Waiting list is full for this ticket type. Only %d spots available.', 'eventin' ), max( 0, $waiting_remaining ) ), |
| 1444 |
['status' => 422] |
| 1445 |
); |
| 1446 |
} |
| 1447 |
} else { |
| 1448 |
if ( $available > 0 && $ticket['ticket_quantity'] > $ticket_left ) { |
| 1449 |
return new WP_Error( 'ticket_limit', __( 'The ticket limit has been exceeded', 'eventin' ), ['status' => 422] ); |
| 1450 |
} |
| 1451 |
} |
| 1452 |
} |
| 1453 |
|
| 1454 |
return true; |
| 1455 |
} |
| 1456 |
} |
| 1457 |
|
| 1458 |
if ( ! function_exists( 'etn_validate_ticket_quantity' ) ) { |
| 1459 |
/** |
| 1460 |
* Validate ticket quantity against min/max per ticket variant |
| 1461 |
* |
| 1462 |
* @param array $order_tickets Order ticket list |
| 1463 |
* @param integer $event_id Event id |
| 1464 |
* |
| 1465 |
* @return bool | WP_Error |
| 1466 |
*/ |
| 1467 |
function etn_validate_ticket_quantity( $order_tickets, $event_id ) { |
| 1468 |
$event = new Event_Model( $event_id ); |
| 1469 |
|
| 1470 |
foreach ( $order_tickets as $ticket ) { |
| 1471 |
$event_ticket = $event->get_ticket( $ticket['ticket_slug'] ); |
| 1472 |
|
| 1473 |
if ( ! $event_ticket ) { |
| 1474 |
continue; |
| 1475 |
} |
| 1476 |
|
| 1477 |
$qty = intval( $ticket['ticket_quantity'] ); |
| 1478 |
$min = intval( $event_ticket['etn_min_ticket'] ?? 0 ); |
| 1479 |
$max = intval( $event_ticket['etn_max_ticket'] ?? 0 ); |
| 1480 |
|
| 1481 |
// When no explicit per-order max is configured, fall back to the |
| 1482 |
// ticket's total capacity so requests cannot exceed what the event |
| 1483 |
// can ever fulfill (relevant for waiting-list orders where the |
| 1484 |
// normal stock check is bypassed). |
| 1485 |
if ( $max === 0 ) { |
| 1486 |
$available = intval( $event_ticket['etn_avaiilable_tickets'] ?? 0 ); |
| 1487 |
if ( $available > 0 ) { |
| 1488 |
$max = $available; |
| 1489 |
} |
| 1490 |
} |
| 1491 |
|
| 1492 |
if ( $min > 0 && $qty < $min ) { |
| 1493 |
return new WP_Error( |
| 1494 |
'min_ticket_quantity', |
| 1495 |
// translators: %d is the minimum ticket quantity required for this ticket type. |
| 1496 |
sprintf( __( 'Minimum %d tickets required for this ticket type.', 'eventin' ), $min ), |
| 1497 |
['status' => 422] |
| 1498 |
); |
| 1499 |
} |
| 1500 |
|
| 1501 |
if ( $max > 0 && $qty > $max ) { |
| 1502 |
return new WP_Error( |
| 1503 |
'max_ticket_quantity', |
| 1504 |
// translators: %d is the maximum ticket quantity allowed for this ticket type. |
| 1505 |
sprintf( __( 'Maximum %d tickets allowed for this ticket type.', 'eventin' ), $max ), |
| 1506 |
['status' => 422] |
| 1507 |
); |
| 1508 |
} |
| 1509 |
} |
| 1510 |
|
| 1511 |
return true; |
| 1512 |
} |
| 1513 |
} |
| 1514 |
|
| 1515 |
if ( ! function_exists( 'etn_get_waiting_list_counts_by_slug' ) ) { |
| 1516 |
/** |
| 1517 |
* Return ticket quantities on the waiting list for an event, keyed by ticket slug. |
| 1518 |
* Counts all orders that carry the is_from_waiting_list meta flag. |
| 1519 |
* |
| 1520 |
* @param integer $event_id |
| 1521 |
* |
| 1522 |
* @return array [ ticket_slug => total_quantity, ... ] |
| 1523 |
*/ |
| 1524 |
function etn_get_waiting_list_counts_by_slug( $event_id ) { |
| 1525 |
$query = new WP_Query( [ |
| 1526 |
'post_type' => 'etn-order', |
| 1527 |
'post_status' => 'any', |
| 1528 |
'posts_per_page' => -1, |
| 1529 |
'fields' => 'ids', |
| 1530 |
'meta_query' => [ |
| 1531 |
[ |
| 1532 |
'key' => 'event_id', |
| 1533 |
'value' => $event_id, |
| 1534 |
], |
| 1535 |
[ |
| 1536 |
'key' => 'is_from_waiting_list', |
| 1537 |
'value' => '1', |
| 1538 |
], |
| 1539 |
], |
| 1540 |
] ); |
| 1541 |
|
| 1542 |
$counts = []; |
| 1543 |
foreach ( $query->posts as $order_id ) { |
| 1544 |
$tickets = etn_safe_decode( get_post_meta( $order_id, 'tickets', true ) ); |
| 1545 |
if ( ! is_array( $tickets ) ) { |
| 1546 |
continue; |
| 1547 |
} |
| 1548 |
foreach ( $tickets as $ticket ) { |
| 1549 |
$slug = $ticket['ticket_slug'] ?? ''; |
| 1550 |
$qty = intval( $ticket['ticket_quantity'] ?? 0 ); |
| 1551 |
if ( $slug ) { |
| 1552 |
$counts[ $slug ] = ( $counts[ $slug ] ?? 0 ) + $qty; |
| 1553 |
} |
| 1554 |
} |
| 1555 |
} |
| 1556 |
|
| 1557 |
return $counts; |
| 1558 |
} |
| 1559 |
} |
| 1560 |
|
| 1561 |
if ( ! function_exists( 'etn_is_tickets_sold_out' ) ) { |
| 1562 |
/** |
| 1563 |
* Check whether the requested tickets are sold out (stock exhausted). |
| 1564 |
* |
| 1565 |
* Returns true when every requested ticket type has no remaining stock, |
| 1566 |
* or a WP_Error naming the ticket type(s) that still have availability. |
| 1567 |
* Tickets with no capacity limit (available = 0) are treated as unlimited |
| 1568 |
* and therefore never considered sold out. |
| 1569 |
* |
| 1570 |
* @param integer $event_id Event ID. |
| 1571 |
* @param array $order_tickets Tickets array from the order request. |
| 1572 |
* |
| 1573 |
* @return true|WP_Error |
| 1574 |
*/ |
| 1575 |
function etn_is_tickets_sold_out( $event_id, $order_tickets ) { |
| 1576 |
$event = new Event_Model( $event_id ); |
| 1577 |
$sold_tickets = (array) Helper::etn_get_sold_tickets_by_event( $event_id ); |
| 1578 |
|
| 1579 |
$enable_global_stock = get_post_meta( $event_id, 'etn_enable_global_stock', true ); |
| 1580 |
$global_stock = intval( get_post_meta( $event_id, 'etn_global_stock', true ) ); |
| 1581 |
|
| 1582 |
if ( $enable_global_stock && $global_stock > 0 ) { |
| 1583 |
$total_sold = array_sum( $sold_tickets ); |
| 1584 |
$ticket_vars = $event->etn_ticket_variations; |
| 1585 |
$total_pending = 0; |
| 1586 |
if ( is_array( $ticket_vars ) ) { |
| 1587 |
foreach ( $ticket_vars as $ticket_var ) { |
| 1588 |
$total_pending += intval( $ticket_var['pending'] ?? 0 ); |
| 1589 |
} |
| 1590 |
} |
| 1591 |
$remaining = $global_stock - $total_sold - $total_pending; |
| 1592 |
if ( $remaining > 0 ) { |
| 1593 |
return new WP_Error( |
| 1594 |
'tickets_available', |
| 1595 |
sprintf( |
| 1596 |
/* translators: %d: number of tickets still available */ |
| 1597 |
__( 'Tickets are still available (%d remaining). You can only join the waiting list when the event is sold out.', 'eventin' ), |
| 1598 |
$remaining |
| 1599 |
), |
| 1600 |
[ 'status' => 400 ] |
| 1601 |
); |
| 1602 |
} |
| 1603 |
return true; |
| 1604 |
} |
| 1605 |
|
| 1606 |
// Per-ticket check. |
| 1607 |
foreach ( $order_tickets as $ticket ) { |
| 1608 |
$event_ticket = $event->get_ticket( $ticket['ticket_slug'] ); |
| 1609 |
|
| 1610 |
if ( ! $event_ticket ) { |
| 1611 |
continue; |
| 1612 |
} |
| 1613 |
|
| 1614 |
$available = intval( $event_ticket['etn_avaiilable_tickets'] ?? 0 ); |
| 1615 |
|
| 1616 |
// Skip tickets with no capacity limit (unlimited). |
| 1617 |
if ( $available <= 0 ) { |
| 1618 |
continue; |
| 1619 |
} |
| 1620 |
|
| 1621 |
$sold = intval( $sold_tickets[ $ticket['ticket_slug'] ] ?? 0 ); |
| 1622 |
$pending = intval( $event_ticket['pending'] ?? 0 ); |
| 1623 |
$left = $available - $sold - $pending; |
| 1624 |
|
| 1625 |
if ( $left > 0 ) { |
| 1626 |
return new WP_Error( |
| 1627 |
'tickets_available', |
| 1628 |
sprintf( |
| 1629 |
/* translators: 1: ticket name, 2: number of tickets still available */ |
| 1630 |
__( 'Tickets are still available for "%1$s" (%2$d remaining). You can only join the waiting list when tickets are sold out.', 'eventin' ), |
| 1631 |
$event_ticket['etn_ticket_name'] ?? $ticket['ticket_slug'], |
| 1632 |
$left |
| 1633 |
), |
| 1634 |
[ 'status' => 400 ] |
| 1635 |
); |
| 1636 |
} |
| 1637 |
} |
| 1638 |
|
| 1639 |
return true; |
| 1640 |
} |
| 1641 |
} |
| 1642 |
|
| 1643 |
if ( ! function_exists( 'etn_validate_seat_ids' ) ) { |
| 1644 |
/** |
| 1645 |
* Validate seat ids |
| 1646 |
* |
| 1647 |
* @param array $seat_ids Seat ids |
| 1648 |
* |
| 1649 |
* @return bool | WP_Error |
| 1650 |
*/ |
| 1651 |
function etn_validate_seat_ids( $event_id, $seat_ids ) { |
| 1652 |
$booked_seats = etn_safe_decode( get_post_meta( $event_id, '_etn_seat_unique_id', true )); |
| 1653 |
$already_booked_seats = $booked_seats ? explode(',', $booked_seats) : []; |
| 1654 |
$pending_seats = etn_safe_decode( get_post_meta( $event_id, 'pending_seats', true )); |
| 1655 |
if ( empty( $pending_seats ) ) { |
| 1656 |
$pending_seats = []; |
| 1657 |
} |
| 1658 |
$is_enable_payment_timer = etn_get_option( 'ticket_purchase_timer_enable', 'off' ); |
| 1659 |
|
| 1660 |
foreach ( $seat_ids as $seat_id ) { |
| 1661 |
// need to handle the corner in rush condition |
| 1662 |
// if ( !in_array( $seat_id, $pending_seats ) && $is_enable_payment_timer == 'on') { |
| 1663 |
// return new WP_Error( 'seat_limit', __( 'The requested seat is already booked, please select another seat', 'eventin' ), ['status' => 422] ); |
| 1664 |
// } |
| 1665 |
if ( in_array( $seat_id, $already_booked_seats ) ) { |
| 1666 |
return new WP_Error( 'seat_limit', __( 'The requested seat is already booked', 'eventin' ), ['status' => 422] ); |
| 1667 |
} |
| 1668 |
} |
| 1669 |
|
| 1670 |
return true; |
| 1671 |
} |
| 1672 |
} |
| 1673 |
|
| 1674 |
if ( ! function_exists('etn_humanize_number') ) { |
| 1675 |
|
| 1676 |
function etn_humanize_number( $number ) { |
| 1677 |
if (!is_numeric($number)) { |
| 1678 |
return $number; |
| 1679 |
} |
| 1680 |
|
| 1681 |
$number = (int)$number; |
| 1682 |
|
| 1683 |
if ($number >= 1000000000) { |
| 1684 |
return round($number / 1000000000, 1) . 'b'; |
| 1685 |
} |
| 1686 |
|
| 1687 |
if ($number >= 1000000) { |
| 1688 |
return round($number / 1000000, 1) . 'm'; |
| 1689 |
} |
| 1690 |
|
| 1691 |
if ($number >= 1000) { |
| 1692 |
return round($number / 1000, 1) . 'k'; |
| 1693 |
} |
| 1694 |
|
| 1695 |
return $number; |
| 1696 |
} |
| 1697 |
} |
| 1698 |
|
| 1699 |
|
| 1700 |
|
| 1701 |
if ( ! function_exists('is_event_template_builder') ) { |
| 1702 |
|
| 1703 |
function is_event_template_builder() { |
| 1704 |
$current_post_id = get_the_ID(); |
| 1705 |
|
| 1706 |
$post_type = get_post_type($current_post_id); |
| 1707 |
|
| 1708 |
if ( $post_type != 'etn-template' ) { |
| 1709 |
return false; |
| 1710 |
} |
| 1711 |
|
| 1712 |
return $current_post_id; |
| 1713 |
} |
| 1714 |
} |
| 1715 |
|
| 1716 |
if ( ! function_exists('get_first_published_event') ) { |
| 1717 |
|
| 1718 |
function get_first_published_event() { |
| 1719 |
$query = new WP_Query( [ |
| 1720 |
'post_type' => 'etn', |
| 1721 |
'post_status' => 'publish', |
| 1722 |
'posts_per_page' => 1, |
| 1723 |
'orderby' => 'date', |
| 1724 |
'order' => 'ASC' |
| 1725 |
] ); |
| 1726 |
|
| 1727 |
$first_post_id = $query->have_posts() ? $query->posts[0]->ID : 0; |
| 1728 |
|
| 1729 |
return $first_post_id; |
| 1730 |
} |
| 1731 |
} |
| 1732 |
|
| 1733 |
if ( ! function_exists( 'etn_get_selected_template_builder' ) ) { |
| 1734 |
/** |
| 1735 |
* Get name of the selected template builder |
| 1736 |
* If selected template builder is deactive or null (not selected) then return emptry string |
| 1737 |
*/ |
| 1738 |
function etn_get_selected_template_builder() { |
| 1739 |
$selected_template_builder = etn_get_option( 'selected_template_builder' ) ?? ''; |
| 1740 |
|
| 1741 |
if ( $selected_template_builder == 'elementor' ) { |
| 1742 |
if ( class_exists( '\Elementor\Plugin' ) ) { |
| 1743 |
return 'elementor'; |
| 1744 |
} |
| 1745 |
|
| 1746 |
return ''; |
| 1747 |
} |
| 1748 |
|
| 1749 |
return $selected_template_builder; |
| 1750 |
} |
| 1751 |
} |
| 1752 |
|
| 1753 |
if ( ! function_exists( 'etn_get_static_event_templates' ) ) { |
| 1754 |
/** |
| 1755 |
* Returns list of static event (landing page) templates. |
| 1756 |
* |
| 1757 |
* @return array |
| 1758 |
*/ |
| 1759 |
function etn_get_static_event_templates() { |
| 1760 |
return \Eventin\Template\StaticTemplateConfig::by_type( 'event' ); |
| 1761 |
} |
| 1762 |
} |
| 1763 |
|
| 1764 |
if ( ! function_exists( 'etn_get_static_ticket_templates' ) ) { |
| 1765 |
/** |
| 1766 |
* Returns list of static ticket templates. |
| 1767 |
* |
| 1768 |
* @return array |
| 1769 |
*/ |
| 1770 |
function etn_get_static_ticket_templates() { |
| 1771 |
return \Eventin\Template\StaticTemplateConfig::by_type( 'ticket' ); |
| 1772 |
} |
| 1773 |
} |
| 1774 |
|
| 1775 |
if ( ! function_exists( 'etn_get_static_certificate_templates' ) ) { |
| 1776 |
/** |
| 1777 |
* Returns list of static certificate templates. |
| 1778 |
* |
| 1779 |
* @return array |
| 1780 |
*/ |
| 1781 |
function etn_get_static_certificate_templates() { |
| 1782 |
return \Eventin\Template\StaticTemplateConfig::by_type( 'certificate' ); |
| 1783 |
} |
| 1784 |
} |
| 1785 |
|
| 1786 |
if ( ! function_exists( 'etn_get_static_speaker_templates' ) ) { |
| 1787 |
/** |
| 1788 |
* Returns list of static speaker templates. |
| 1789 |
* |
| 1790 |
* @return array |
| 1791 |
*/ |
| 1792 |
function etn_get_static_speaker_templates() { |
| 1793 |
return \Eventin\Template\StaticTemplateConfig::by_type( 'speaker' ); |
| 1794 |
} |
| 1795 |
} |
| 1796 |
|
| 1797 |
if ( ! function_exists( 'etn_get_static_templates_by_type' ) ) { |
| 1798 |
/** |
| 1799 |
* Returns static templates for a given type, or every type when empty. |
| 1800 |
* |
| 1801 |
* Backed by \Eventin\Template\StaticTemplateConfig — edit that config to |
| 1802 |
* change the starter template list or its free/pro flags. |
| 1803 |
* |
| 1804 |
* @param string $type |
| 1805 |
* |
| 1806 |
* @return array |
| 1807 |
*/ |
| 1808 |
function etn_get_static_templates_by_type( $type = '' ) { |
| 1809 |
return \Eventin\Template\StaticTemplateConfig::by_type( $type ); |
| 1810 |
} |
| 1811 |
} |
| 1812 |
|
| 1813 |
if ( ! function_exists( 'etn_get_valid_event_socials' ) ) { |
| 1814 |
/** |
| 1815 |
* Filter an event's social links down to the renderable ones. |
| 1816 |
* |
| 1817 |
* The event form seeds an empty row so the repeater always has an input to |
| 1818 |
* type into, so `etn_event_socials` can hold entries with no icon and no |
| 1819 |
* URL at all. A link needs both to be renderable: without a URL there is |
| 1820 |
* nowhere to go, and without an icon the <a> paints as an empty chip (the |
| 1821 |
* bare circle next to "Share:"). Every render path filters through this. |
| 1822 |
* |
| 1823 |
* @param mixed $socials Raw etn_event_socials meta. |
| 1824 |
* |
| 1825 |
* @return array Entries that have both an icon and a URL. Keys are reindexed. |
| 1826 |
*/ |
| 1827 |
function etn_get_valid_event_socials( $socials ) { |
| 1828 |
if ( ! is_array( $socials ) ) { |
| 1829 |
return []; |
| 1830 |
} |
| 1831 |
|
| 1832 |
$valid = array_filter( |
| 1833 |
$socials, |
| 1834 |
function ( $social ) { |
| 1835 |
return is_array( $social ) |
| 1836 |
&& ! empty( $social['icon'] ) |
| 1837 |
&& ! empty( $social['etn_social_url'] ); |
| 1838 |
} |
| 1839 |
); |
| 1840 |
|
| 1841 |
return array_values( $valid ); |
| 1842 |
} |
| 1843 |
} |
| 1844 |
|
| 1845 |
if ( ! function_exists( 'etn_readable_post_text' ) ) { |
| 1846 |
/** |
| 1847 |
* An event's body or excerpt, blanked when the visitor may not read it. |
| 1848 |
* |
| 1849 |
* Listings show a snippet of each event: the calendar endpoint, the related |
| 1850 |
* events strip, the Elementor calendar widget, the .ics download. A |
| 1851 |
* password-protected event is still `publish`, so it turns up in all of them |
| 1852 |
* like any other event — and printing its text raw handed out exactly what |
| 1853 |
* the password exists to hide. |
| 1854 |
* |
| 1855 |
* WordPress guards its own equivalents this way: get_the_content() returns |
| 1856 |
* the password form and get_the_excerpt() returns a placeholder when |
| 1857 |
* post_password_required() is true. These call sites read the column |
| 1858 |
* directly, so they need the test spelled out. |
| 1859 |
* |
| 1860 |
* @param int|\WP_Post $post Post or post id. |
| 1861 |
* @param string $field 'post_content' (default) or 'post_excerpt'. |
| 1862 |
* |
| 1863 |
* @return string Empty string when the visitor may not see it. |
| 1864 |
*/ |
| 1865 |
function etn_readable_post_text( $post, $field = 'post_content' ) { |
| 1866 |
$post = get_post( $post ); |
| 1867 |
|
| 1868 |
if ( ! $post ) { |
| 1869 |
return ''; |
| 1870 |
} |
| 1871 |
|
| 1872 |
if ( ! \Eventin\AccessControl\Ownership::can_read_protected_content( $post ) ) { |
| 1873 |
return ''; |
| 1874 |
} |
| 1875 |
|
| 1876 |
return (string) ( 'post_excerpt' === $field ? $post->post_excerpt : $post->post_content ); |
| 1877 |
} |
| 1878 |
} |
| 1879 |
|