1313 lines
38 KiB
PHP
1313 lines
38 KiB
PHP
<?php
|
|
/**
|
|
* Simply Schedule Appointments Utils.
|
|
*
|
|
* @since 0.0.3
|
|
* @package Simply_Schedule_Appointments
|
|
*/
|
|
use League\Period\Period;
|
|
|
|
/**
|
|
* Simply Schedule Appointments Utils.
|
|
*
|
|
* @since 0.0.3
|
|
*/
|
|
class SSA_Utils {
|
|
/**
|
|
* Parent plugin class.
|
|
*
|
|
* @since 0.0.3
|
|
*
|
|
* @var Simply_Schedule_Appointments
|
|
*/
|
|
protected $plugin = null;
|
|
|
|
protected $server_default_timezone_before_ssa;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @since 0.0.3
|
|
*
|
|
* @param Simply_Schedule_Appointments $plugin Main plugin object.
|
|
*/
|
|
public function __construct( $plugin ) {
|
|
$this->plugin = $plugin;
|
|
$this->hooks();
|
|
}
|
|
|
|
/**
|
|
* Initiate our hooks.
|
|
*
|
|
* @since 0.0.3
|
|
*/
|
|
public function hooks() {
|
|
|
|
}
|
|
|
|
public function defensive_timezone_fix() {
|
|
if ( 'UTC' === date_default_timezone_get() ) {
|
|
return;
|
|
}
|
|
|
|
$this->server_default_timezone_before_ssa = date_default_timezone_get();
|
|
|
|
// We know that setting the default_timezone on a server is bad practice
|
|
// WordPress expects it to be UTC to function properly
|
|
// Our plugin also expects it to be UTC to function properly
|
|
// Unfortunately we have found that some plugins do change the default timezone
|
|
// We only call this function as a defensive measure, so SSA can co-exist with plugins
|
|
// that set the timezone. Looking at you...
|
|
//
|
|
// * Ajax Event Calendar plugin [https://wordpress.org/support/plugin/ajax-event-calendar]
|
|
// *** already removed from the wordpress.org repository
|
|
//
|
|
// * Series Engine plugin [https://seriesengine.com/]
|
|
// ** Pro plugin not available on wordpress.org
|
|
//
|
|
// Here's our approach to addressing this issue:
|
|
// We ONLY set the timezone to UTC if it's something different
|
|
//
|
|
// We feel that it should be forced to UTC to adhere to WordPress standards,
|
|
// but that will probably break users' sites running these problematic plugins
|
|
//
|
|
// To try and play nicely with others and to protect the user, we will call our
|
|
// defensive_timezone_fix() before SSA does anything where we rely on a UTC timezone
|
|
// at the end of our functions, we will call defensive_timezone_reset() so we'll put it
|
|
// back to whatever the server already had set.
|
|
//
|
|
// We see this as the only way to co-exist with these problematic plugins and simplify
|
|
// life for the user. If there is a better approach, please get in touch.
|
|
// We'd love to remove this code :)
|
|
date_default_timezone_set( 'UTC' );
|
|
}
|
|
public function defensive_timezone_reset() {
|
|
if ( empty( $this->server_default_timezone_before_ssa ) || 'UTC' === $this->server_default_timezone_before_ssa ) {
|
|
return;
|
|
}
|
|
|
|
// We know that setting the default_timezone on a server is bad practice
|
|
// ^^^ See note above in defensive_timezone_fix() ^^^
|
|
date_default_timezone_set( $this->server_default_timezone_before_ssa );
|
|
}
|
|
|
|
public static function site_unique_hash( $string ) {
|
|
if (defined('SSA_AUTH_SALT')) {
|
|
$salt = SSA_AUTH_SALT;
|
|
} else if ( defined( 'AUTH_SALT' ) ) {
|
|
$salt = AUTH_SALT;
|
|
} else {
|
|
$salt = 'M+mZDQYJlrHoRZ0OfE1ESGG5T5CgPMOgOub25eOmwJYdPLmiNLbKwXYGQfG0pkF5YCw45DVtwaWREx3Jr4hILB';
|
|
}
|
|
|
|
return hash_hmac('md5', $string, $salt);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @param [type] $string
|
|
* @return void
|
|
*/
|
|
public static function deprecated_hash( $string ) {
|
|
if ( defined( 'SSA_AUTH_SALT' ) ) {
|
|
$salt = SSA_AUTH_SALT;
|
|
} else {
|
|
$salt = '6U2aRk6oGvAZAEXstbFNMppRF=D|H.NX!-gU:-aXGVH<)8kcF~FPor5{Z<SFr~wKz';
|
|
}
|
|
|
|
return hash_hmac('md5', $string, $salt);
|
|
}
|
|
|
|
public static function get_home_id() {
|
|
return self::site_unique_hash( get_home_url() );
|
|
}
|
|
|
|
public static function is_assoc_array( array $arr ) {
|
|
if ( array() === $arr ) return false;
|
|
return array_keys( $arr ) !== range( 0, count( $arr ) - 1 );
|
|
}
|
|
|
|
public static function arrays_assoc_are_equal( $a, $b ) {
|
|
if ( ! is_array( $a ) || ! is_array( $b ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( count( $a ) !== count( $b ) ) {
|
|
return false;
|
|
}
|
|
|
|
foreach ( $a as $key => $value ) {
|
|
if ( ! array_key_exists( $key, $b ) ) {
|
|
return false;
|
|
}
|
|
if ( is_array( $value ) ) {
|
|
if ( ! is_array( $b[ $key ] ) ) {
|
|
return false;
|
|
}
|
|
if ( ! self::arrays_assoc_are_equal( $value, $b[ $key ] ) ) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if ( $value !== $b[ $key ] ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static function array_key( $array, $key ) {
|
|
if ( isset( $array[ $key ] ) ) {
|
|
return $array[ $key ];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static function datetime( $time='now' ) {
|
|
if ( $time instanceof DateTimeImmutable ) {
|
|
return $time;
|
|
}
|
|
|
|
if ( 0 === strpos( $time, 'Invalid' ) ) {
|
|
ssa_debug_log( 'SSA_Utils::datetime() `Invalid Date` detected' );
|
|
// $time = 'now';
|
|
return null; // TODO: handle error state gracefully
|
|
} else if ( empty( $time ) ) {
|
|
$time = 'now';
|
|
}
|
|
|
|
$timezone = new DateTimeZone( 'UTC' );
|
|
|
|
return new DateTimeImmutable( $time, $timezone );
|
|
}
|
|
|
|
public static function ceil_datetime( DateTimeImmutable $datetime, $mins = 5 ) {
|
|
$seconds = $mins * 60;
|
|
$time = ( ceil( $datetime->getTimestamp() / $seconds ) ) * $seconds;
|
|
|
|
return $datetime->setTimestamp( $time );
|
|
}
|
|
|
|
public static function floor_datetime( DateTimeImmutable $datetime, $mins = 5 ) {
|
|
$seconds = $mins * 60;
|
|
$time = ( floor( $datetime->getTimestamp() / $seconds ) ) * $seconds;
|
|
|
|
return $datetime->setTimestamp( $time );
|
|
}
|
|
|
|
/**
|
|
* Create DateTimeZone safely, handling PHP 8.3+ deprecated timezone names.
|
|
*
|
|
* @since 5.8.8
|
|
* @param string $timezone_string Timezone identifier
|
|
* @param DateTimeZone|null $fallback Optional fallback timezone if creation fails
|
|
* @return DateTimeZone
|
|
*/
|
|
public static function safe_timezone( $timezone_string, $fallback = null ) {
|
|
if ( $timezone_string instanceof DateTimeZone ) {
|
|
return $timezone_string;
|
|
}
|
|
|
|
try {
|
|
return new DateTimeZone( $timezone_string );
|
|
} catch ( Exception $e ) {
|
|
// Map deprecated timezone names to modern equivalents (PHP 8.3+)
|
|
// Based on IANA timezone database backward compatibility file
|
|
$deprecated_timezones = array(
|
|
// US timezones
|
|
'US/Alaska' => 'America/Anchorage',
|
|
'US/Aleutian' => 'America/Adak',
|
|
'US/Arizona' => 'America/Phoenix',
|
|
'US/Central' => 'America/Chicago',
|
|
'US/Eastern' => 'America/New_York',
|
|
'US/East-Indiana' => 'America/Indiana/Indianapolis',
|
|
'US/Hawaii' => 'Pacific/Honolulu',
|
|
'US/Indiana-Starke'=> 'America/Indiana/Knox',
|
|
'US/Michigan' => 'America/Detroit',
|
|
'US/Mountain' => 'America/Denver',
|
|
'US/Pacific' => 'America/Los_Angeles',
|
|
'US/Samoa' => 'Pacific/Pago_Pago',
|
|
|
|
// Canada
|
|
'Canada/Atlantic' => 'America/Halifax',
|
|
'Canada/Central' => 'America/Winnipeg',
|
|
'Canada/Eastern' => 'America/Toronto',
|
|
'Canada/Mountain' => 'America/Edmonton',
|
|
'Canada/Newfoundland' => 'America/St_Johns',
|
|
'Canada/Pacific' => 'America/Vancouver',
|
|
'Canada/Saskatchewan' => 'America/Regina',
|
|
'Canada/Yukon' => 'America/Whitehorse',
|
|
|
|
// Asia
|
|
'Asia/Calcutta' => 'Asia/Kolkata',
|
|
'Asia/Katmandu' => 'Asia/Kathmandu',
|
|
'Asia/Saigon' => 'Asia/Ho_Chi_Minh',
|
|
'Asia/Chongqing' => 'Asia/Shanghai',
|
|
'Asia/Chungking' => 'Asia/Shanghai',
|
|
'Asia/Dacca' => 'Asia/Dhaka',
|
|
'Asia/Harbin' => 'Asia/Shanghai',
|
|
'Asia/Kashgar' => 'Asia/Urumqi',
|
|
'Asia/Macao' => 'Asia/Macau',
|
|
'Asia/Tel_Aviv' => 'Asia/Jerusalem',
|
|
'Asia/Thimbu' => 'Asia/Thimphu',
|
|
'Asia/Ujung_Pandang' => 'Asia/Makassar',
|
|
'Asia/Ulan_Bator' => 'Asia/Ulaanbaatar',
|
|
|
|
// Europe
|
|
'Europe/Belfast' => 'Europe/London',
|
|
'Europe/Tiraspol' => 'Europe/Chisinau',
|
|
'Europe/Nicosia' => 'Asia/Nicosia',
|
|
|
|
// Africa
|
|
'Africa/Asmera' => 'Africa/Asmara',
|
|
'Africa/Timbuktu' => 'Africa/Bamako',
|
|
|
|
// America
|
|
'America/Argentina/ComodRivadavia' => 'America/Argentina/Catamarca',
|
|
'America/Atka' => 'America/Adak',
|
|
'America/Buenos_Aires' => 'America/Argentina/Buenos_Aires',
|
|
'America/Catamarca' => 'America/Argentina/Catamarca',
|
|
'America/Coral_Harbour' => 'America/Atikokan',
|
|
'America/Cordoba' => 'America/Argentina/Cordoba',
|
|
'America/Ensenada' => 'America/Tijuana',
|
|
'America/Fort_Wayne' => 'America/Indiana/Indianapolis',
|
|
'America/Indianapolis' => 'America/Indiana/Indianapolis',
|
|
'America/Jujuy' => 'America/Argentina/Jujuy',
|
|
'America/Knox_IN' => 'America/Indiana/Knox',
|
|
'America/Louisville' => 'America/Kentucky/Louisville',
|
|
'America/Mendoza' => 'America/Argentina/Mendoza',
|
|
'America/Porto_Acre' => 'America/Rio_Branco',
|
|
'America/Rosario' => 'America/Argentina/Cordoba',
|
|
'America/Virgin' => 'America/St_Thomas',
|
|
|
|
// Australia
|
|
'Australia/ACT' => 'Australia/Sydney',
|
|
'Australia/Canberra' => 'Australia/Sydney',
|
|
'Australia/LHI' => 'Australia/Lord_Howe',
|
|
'Australia/NSW' => 'Australia/Sydney',
|
|
'Australia/North' => 'Australia/Darwin',
|
|
'Australia/Queensland' => 'Australia/Brisbane',
|
|
'Australia/South' => 'Australia/Adelaide',
|
|
'Australia/Tasmania' => 'Australia/Hobart',
|
|
'Australia/Victoria' => 'Australia/Melbourne',
|
|
'Australia/West' => 'Australia/Perth',
|
|
'Australia/Yancowinna' => 'Australia/Broken_Hill',
|
|
|
|
// Brazil
|
|
'Brazil/Acre' => 'America/Rio_Branco',
|
|
'Brazil/DeNoronha' => 'America/Noronha',
|
|
'Brazil/East' => 'America/Sao_Paulo',
|
|
'Brazil/West' => 'America/Manaus',
|
|
|
|
// Chile
|
|
'Chile/Continental' => 'America/Santiago',
|
|
'Chile/EasterIsland' => 'Pacific/Easter',
|
|
|
|
// Pacific
|
|
'Pacific/Ponape' => 'Pacific/Pohnpei',
|
|
'Pacific/Samoa' => 'Pacific/Pago_Pago',
|
|
'Pacific/Truk' => 'Pacific/Chuuk',
|
|
'Pacific/Yap' => 'Pacific/Chuuk',
|
|
|
|
// Country shortcuts
|
|
'Egypt' => 'Africa/Cairo',
|
|
'Eire' => 'Europe/Dublin',
|
|
'GB' => 'Europe/London',
|
|
'GB-Eire' => 'Europe/London',
|
|
'Greenwich' => 'Etc/GMT',
|
|
'Hongkong' => 'Asia/Hong_Kong',
|
|
'Iceland' => 'Atlantic/Reykjavik',
|
|
'Iran' => 'Asia/Tehran',
|
|
'Israel' => 'Asia/Jerusalem',
|
|
'Jamaica' => 'America/Jamaica',
|
|
'Japan' => 'Asia/Tokyo',
|
|
'Kwajalein' => 'Pacific/Kwajalein',
|
|
'Libya' => 'Africa/Tripoli',
|
|
'NZ' => 'Pacific/Auckland',
|
|
'NZ-CHAT' => 'Pacific/Chatham',
|
|
'Navajo' => 'America/Denver',
|
|
'PRC' => 'Asia/Shanghai',
|
|
'Poland' => 'Europe/Warsaw',
|
|
'Portugal' => 'Europe/Lisbon',
|
|
'ROC' => 'Asia/Taipei',
|
|
'ROK' => 'Asia/Seoul',
|
|
'Singapore' => 'Asia/Singapore',
|
|
'Turkey' => 'Europe/Istanbul',
|
|
'UCT' => 'Etc/UTC',
|
|
'Universal' => 'Etc/UTC',
|
|
'W-SU' => 'Europe/Moscow',
|
|
'Zulu' => 'Etc/UTC',
|
|
|
|
// Mexico
|
|
'Mexico/BajaNorte' => 'America/Tijuana',
|
|
'Mexico/BajaSur' => 'America/Mazatlan',
|
|
'Mexico/General' => 'America/Mexico_City',
|
|
);
|
|
|
|
if ( isset( $deprecated_timezones[ $timezone_string ] ) ) {
|
|
try {
|
|
return new DateTimeZone( $deprecated_timezones[ $timezone_string ] );
|
|
} catch ( Exception $e2 ) {
|
|
// Mapped timezone also failed - this shouldn't happen with valid mappings
|
|
error_log( sprintf(
|
|
'SSA: Deprecated timezone "%s" mapped to "%s" but mapping also failed: %s',
|
|
$timezone_string,
|
|
$deprecated_timezones[ $timezone_string ],
|
|
$e2->getMessage()
|
|
) );
|
|
// Now Fall through to fallback logic
|
|
}
|
|
}
|
|
|
|
// Return fallback or UTC as last resort
|
|
if ( $fallback instanceof DateTimeZone ) {
|
|
return $fallback;
|
|
}
|
|
return new DateTimeZone( 'UTC' );
|
|
}
|
|
}
|
|
|
|
public static function get_datetime_in_utc( $datetime, $datetimezone='UTC' ) {
|
|
if ( ! ( $datetimezone instanceof DateTimeZone ) ) {
|
|
$datetimezone = self::safe_timezone( $datetimezone );
|
|
}
|
|
|
|
if ( ! ( $datetime instanceof DateTimeImmutable ) ) {
|
|
$datetime = new DateTimeImmutable( $datetime, $datetimezone );
|
|
}
|
|
|
|
$datetime = $datetime->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
return $datetime;
|
|
}
|
|
|
|
public static function get_period_in_utc( Period $period ) {
|
|
return self::get_period_in_timezone( $period, new DateTimeZone( 'UTC' ) );
|
|
}
|
|
|
|
public static function get_period_in_timezone( Period $period, DateTimeZone $timezone ) {
|
|
return new Period(
|
|
$period->getStartDate()->setTimezone( $timezone ),
|
|
$period->getEndDate()->setTimezone( $timezone )
|
|
);
|
|
}
|
|
|
|
public function get_datetime_as_local_datetime( $datetime, $appointment_type_id=0, $staff_id = 0, $location_id = 0 ) {
|
|
$local_timezone = $this->get_datetimezone( $appointment_type_id, $staff_id, $location_id );
|
|
|
|
if ( empty( $local_timezone ) || ! ( $local_timezone instanceof DateTimeZone ) ) {
|
|
throw new SSA_Exception( __( 'No $local_timezone defined' ), 500 );
|
|
}
|
|
|
|
if ( $datetime instanceof DateTime ) {
|
|
$datetime = new DateTimeImmutable( $datetime );
|
|
} elseif ( ! ( $datetime instanceof DateTimeImmutable ) ) {
|
|
$utc_timezone = new DateTimeZone( 'UTC' );
|
|
$datetime = new DateTimeImmutable( $datetime, $utc_timezone );
|
|
$datetime = $datetime->setTimezone( $local_timezone );
|
|
} else {
|
|
$datetime = $datetime->setTimezone( $local_timezone );
|
|
}
|
|
|
|
|
|
return $datetime;
|
|
}
|
|
|
|
public function get_datetimezone( $appointment_type_id = 0, $staff_id = 0, $location_id = 0 ) {
|
|
if ( !empty( $staff_id ) ) {
|
|
throw new SSA_Exception( __( 'Staff members are not supported yet' ), 500 );
|
|
} elseif ( !empty( $location_id ) ) {
|
|
throw new SSA_Exception( __( 'Locations are not supported yet' ), 500 );
|
|
} else {
|
|
$local_timezone = $this->plugin->settings_global->get_datetimezone();
|
|
}
|
|
|
|
return $local_timezone;
|
|
}
|
|
|
|
/**
|
|
* Returns datetime format set by the customer on the global settings page.
|
|
*
|
|
* @since 4.9.1
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_localized_date_format_from_settings() {
|
|
$settings = ssa()->settings->get();
|
|
$format = $settings['global']['date_format'] . ' ' . $settings['global']['time_format'];
|
|
|
|
return $format;
|
|
}
|
|
|
|
/**
|
|
* Returns date format set by the customer on the global settings page.
|
|
*
|
|
* @since 4.9.1
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_localized_date_only_format_from_settings() {
|
|
$settings = ssa()->settings->get();
|
|
$format = $settings['global']['date_format'];
|
|
|
|
return $format;
|
|
}
|
|
|
|
/**
|
|
* Returns time format set by the customer on the global settings page.
|
|
*
|
|
* @since 4.9.1
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_localized_time_only_format_from_settings() {
|
|
$settings = ssa()->settings->get();
|
|
$format = $settings['global']['time_format'];
|
|
|
|
return $format;
|
|
}
|
|
|
|
public static function localize_default_date_strings( $php_date_format ) {
|
|
if ( 'F j, Y' === $php_date_format ) {
|
|
$php_date_format = __( 'F j, Y', 'default' );
|
|
} else if ( 'g:i a' === $php_date_format ) {
|
|
$php_date_format = __( 'g:i a', 'default' );
|
|
} else if ( 'F j, Y g:i a' === $php_date_format ) {
|
|
$php_date_format = __( 'F j, Y g:i a', 'default' );
|
|
}
|
|
|
|
return $php_date_format;
|
|
}
|
|
|
|
public static function translate_formatted_date( $formatted_date ) {
|
|
$translations = array();
|
|
|
|
if( !empty( ssa()->translation->programmatic_locale ) && is_string( ssa()->translation->programmatic_locale ) ){
|
|
$mo_file = WP_LANG_DIR . '/' . ssa()->translation->programmatic_locale . '.mo';
|
|
if( ! file_exists( $mo_file ) ){
|
|
// if the WP core language file doesn't exist, try the plugin language file
|
|
$mo_file = WP_LANG_DIR . '/plugins/simply-schedule-appointments-' . ssa()->translation->programmatic_locale . '.mo';
|
|
}
|
|
if ( file_exists( $mo_file ) ) {
|
|
// Make sure the MO class is available.
|
|
if ( ! class_exists( 'MO' ) ) {
|
|
require_once( ABSPATH . WPINC . '/l10n.php' );
|
|
}
|
|
|
|
// Instantiate a new MO object.
|
|
$mo = new MO();
|
|
// Load your specific .mo file.
|
|
// (Replace '/path/to/your/file.mo' with the correct path.)
|
|
if ( $mo->import_from_file( $mo_file ) ) {
|
|
|
|
// Now translate the strings using the loaded .mo file.
|
|
$translations = array(
|
|
'January' => $mo->translate( 'January' ),
|
|
'February' => $mo->translate( 'February' ),
|
|
'March' => $mo->translate( 'March' ),
|
|
'April' => $mo->translate( 'April' ),
|
|
'May' => $mo->translate( 'May' ),
|
|
'June' => $mo->translate( 'June' ),
|
|
'July' => $mo->translate( 'July' ),
|
|
'August' => $mo->translate( 'August' ),
|
|
'September' => $mo->translate( 'September' ),
|
|
'October' => $mo->translate( 'October' ),
|
|
'November' => $mo->translate( 'November' ),
|
|
'December' => $mo->translate( 'December' ),
|
|
|
|
'Monday' => $mo->translate( 'Monday' ),
|
|
'Tuesday' => $mo->translate( 'Tuesday' ),
|
|
'Wednesday' => $mo->translate( 'Wednesday' ),
|
|
'Thursday' => $mo->translate( 'Thursday' ),
|
|
'Friday' => $mo->translate( 'Friday' ),
|
|
'Saturday' => $mo->translate( 'Saturday' ),
|
|
'Sunday' => $mo->translate( 'Sunday' ),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(empty($translations)){
|
|
$translations = array(
|
|
'January' => __( 'January' ),
|
|
'February' => __( 'February' ),
|
|
'March' => __( 'March' ),
|
|
'April' => __( 'April' ),
|
|
'May' => __( 'May' ),
|
|
'June' => __( 'June' ),
|
|
'July' => __( 'July' ),
|
|
'August' => __( 'August' ),
|
|
'September' => __( 'September' ),
|
|
'October' => __( 'October' ),
|
|
'November' => __( 'November' ),
|
|
'December' => __( 'December' ),
|
|
|
|
'Monday' => __( 'Monday' ),
|
|
'Tuesday' => __( 'Tuesday' ),
|
|
'Wednesday' => __( 'Wednesday' ),
|
|
'Thursday' => __( 'Thursday' ),
|
|
'Friday' => __( 'Friday' ),
|
|
'Saturday' => __( 'Saturday' ),
|
|
'Sunday' => __( 'Sunday' ),
|
|
);
|
|
}
|
|
|
|
return str_replace( array_keys( $translations ), array_values( $translations ), $formatted_date );
|
|
|
|
// return strtr( ( string ) $formatted_date, $translations );
|
|
}
|
|
|
|
public static function php_to_moment_format($php_date_format) {
|
|
$php_date_format = self::localize_default_date_strings( $php_date_format );
|
|
|
|
$replacements = array(
|
|
'\\h' => '[h]',
|
|
'\\m\\i\\n' => '[min]',
|
|
'\\m' => '[m]',
|
|
'\\' => '',
|
|
'\\d' => '\\d', // Leave Spanish string 'de' (of) untouched
|
|
'\\e' => '\\e', // Leave Spanish string 'de' (of) untouched
|
|
|
|
'd' => 'DD',
|
|
'D' => 'ddd',
|
|
'j' => 'D',
|
|
'l' => 'dddd',
|
|
'N' => 'E',
|
|
'S' => 'o',
|
|
'w' => 'e',
|
|
'z' => 'DDD',
|
|
'W' => 'W',
|
|
'F' => 'MMMM',
|
|
'm' => 'MM',
|
|
'M' => 'MMM',
|
|
'n' => 'M',
|
|
't' => '', // no equivalent
|
|
'L' => '', // no equivalent
|
|
'o' => 'YYYY',
|
|
'Y' => 'YYYY',
|
|
'y' => 'YY',
|
|
'a' => 'a',
|
|
'A' => 'A',
|
|
'B' => '', // no equivalent
|
|
'g' => 'h',
|
|
'G' => 'H',
|
|
'h' => 'hh',
|
|
'H' => 'HH',
|
|
'i' => 'mm',
|
|
's' => 'ss',
|
|
'u' => 'SSS',
|
|
'e' => 'zz', // deprecated since version 1.6.0 of moment.js
|
|
'I' => '', // no equivalent
|
|
'O' => '', // no equivalent
|
|
'P' => '', // no equivalent
|
|
'T' => '', // no equivalent
|
|
'Z' => '', // no equivalent
|
|
'c' => '', // no equivalent
|
|
'r' => '', // no equivalent
|
|
'U' => 'X',
|
|
);
|
|
|
|
$moment_format = strtr($php_date_format, $replacements);
|
|
|
|
if( isset( $_GET["ssa_locale"] )){
|
|
$moment_format = apply_filters( 'ssa/moment_format', $moment_format, $_GET["ssa_locale"] );
|
|
}
|
|
|
|
return $moment_format;
|
|
}
|
|
|
|
public static function moment_to_php_format($moment_date_format) {
|
|
$replacements = array(
|
|
'DD' => 'd',
|
|
'ddd' => 'D',
|
|
'D' => 'j',
|
|
'dddd' => 'l',
|
|
'E' => 'N',
|
|
'o' => 'S',
|
|
'e' => 'w',
|
|
'DDD' => 'z',
|
|
'W' => 'W',
|
|
'MMMM' => 'F',
|
|
'MM' => 'm',
|
|
'MMM' => 'M',
|
|
'M' => 'n',
|
|
'YYYY' => 'o',
|
|
'YYYY' => 'Y',
|
|
'YY' => 'y',
|
|
'a' => 'a',
|
|
'A' => 'A',
|
|
'h' => 'g',
|
|
'H' => 'G',
|
|
'hh' => 'h',
|
|
'HH' => 'H',
|
|
'mm' => 'i',
|
|
'ss' => 's',
|
|
'SSS' => 'u',
|
|
'zz' => 'e', // deprecated since version 1.6.0 of moment.js
|
|
'X' => 'U',
|
|
);
|
|
|
|
$php_format = strtr($moment_date_format, $replacements);
|
|
|
|
return $php_format;
|
|
}
|
|
|
|
public function get_formatted_date( $date, $appointment_type_id = 0 ){
|
|
$local_date = $this->get_datetime_as_local_datetime( $date, $appointment_type_id );
|
|
$format = self::get_localized_date_format_from_settings();
|
|
$format = self::localize_default_date_strings( $format ) . ' (T)';
|
|
$value = $local_date->format( $format );
|
|
$value = self::translate_formatted_date( $value );
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Define constant if not already set.
|
|
*
|
|
* @param string $name Constant name.
|
|
* @param string|bool $value Constant value.
|
|
*/
|
|
public static function define( $name, $value ) {
|
|
if ( ! defined( $name ) ) {
|
|
define( $name, $value );
|
|
}
|
|
}
|
|
|
|
public static function get_appointment_type( $appointment_type ) {
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = ssa()->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
return false;
|
|
}
|
|
|
|
return $appointment_type;
|
|
}
|
|
|
|
public static function get_query_period( Period $query_period = null ) {
|
|
if ( null === $query_period ) {
|
|
$query_period = SSA_Constants::EPOCH_PERIOD();
|
|
}
|
|
|
|
return $query_period;
|
|
}
|
|
}
|
|
|
|
function ssa_datetime( $time='now' ) {
|
|
return SSA_Utils::datetime( $time );
|
|
}
|
|
|
|
function ssa_gmtstrtotime( $string ) {
|
|
$time = strtotime($string . ' +0000');
|
|
|
|
if ( -1 == $time ) {
|
|
return strtotime($string);
|
|
}
|
|
|
|
return $time;
|
|
}
|
|
|
|
function ssa_defensive_timezone_fix() {
|
|
ssa()->utils->defensive_timezone_fix();
|
|
}
|
|
function ssa_defensive_timezone_reset() {
|
|
ssa()->utils->defensive_timezone_reset();
|
|
}
|
|
|
|
function ssa_debug_log( $var, $debug_level = 1, $label = '', $file = 'debug' ) {
|
|
if ( defined( 'SSA_DEBUG_LOG' ) && empty( SSA_DEBUG_LOG ) ) {
|
|
return;
|
|
}
|
|
|
|
// Store backtrace early for PHP 7.0+ compatibility (before using parameters)
|
|
$backtrace = debug_backtrace();
|
|
|
|
$ssa_debug_level = get_option( 'ssa_debug_level', 10 );
|
|
if ( $debug_level < $ssa_debug_level ) {
|
|
// We want to log really fatal errors (level 10) so the support team can see them when logging in after the fact
|
|
return;
|
|
}
|
|
|
|
$path = ssa()->support_status->get_log_file_path( $file );
|
|
$log_prefix = gmdate( '[Y-m-d H:i:s] ' );
|
|
|
|
if ( empty( $path ) ) {
|
|
return;
|
|
}
|
|
|
|
// Validate indices before access (PHP 8.0+ compatibility)
|
|
|
|
if ( isset( $backtrace[1]['function'] ) ) {
|
|
error_log( PHP_EOL . 'The following is logged from ' . $backtrace[1]['function'] . '()' . PHP_EOL, 3, $path );
|
|
}
|
|
if ( isset( $backtrace[0]['file'], $backtrace[0]['line'] ) ) {
|
|
error_log( 'in ' . $backtrace[0]['file'] . ' line ' . $backtrace[0]['line'] . ':' . PHP_EOL, 3, $path );
|
|
}
|
|
|
|
if ( isset( $backtrace[2]['function'] ) ) {
|
|
error_log( PHP_EOL . '... ' . $backtrace[2]['function'] . '()' . PHP_EOL, 3, $path );
|
|
}
|
|
if ( isset( $backtrace[1]['file'], $backtrace[1]['line'] ) ) {
|
|
error_log( 'in ' . $backtrace[1]['file'] . ' line ' . $backtrace[1]['line'] . ':' . PHP_EOL, 3, $path );
|
|
}
|
|
|
|
if ( isset( $backtrace[3]['function'] ) ) {
|
|
error_log( PHP_EOL . '...... ' . $backtrace[3]['function'] . '()' . PHP_EOL, 3, $path );
|
|
}
|
|
if ( isset( $backtrace[2]['file'], $backtrace[2]['line'] ) ) {
|
|
error_log( 'in ' . $backtrace[2]['file'] . ' line ' . $backtrace[2]['line'] . ':' . PHP_EOL, 3, $path );
|
|
}
|
|
|
|
if ( ! empty( $label ) ) {
|
|
error_log( $log_prefix.$label.PHP_EOL, 3, $path );
|
|
}
|
|
if ( is_string( $var ) ) {
|
|
error_log( $log_prefix.$var.PHP_EOL, 3, $path );
|
|
} else {
|
|
// error_log( $log_prefix.print_r( $var, true ).PHP_EOL, 3, $path );
|
|
}
|
|
}
|
|
|
|
function ssa_int_hash( $string ) {
|
|
return abs( crc32( $string ) );
|
|
}
|
|
|
|
function ssa_get_staff_id( $user_id ) {
|
|
return ssa()->staff_model->get_staff_id_for_user_id( $user_id );
|
|
}
|
|
function ssa_get_current_staff_id() {
|
|
return ssa_get_staff_id( get_current_user_id() );
|
|
}
|
|
|
|
function ssa_cache_get( $key, $group = '', $force = false, &$found = null ) {
|
|
return SSA_Cache::object_cache_get( $key, $group, $force, $found );
|
|
}
|
|
function ssa_cache_set( $key, $data, $group = '', $expire = 0 ) {
|
|
return SSA_Cache::object_cache_set( $key, $data, $group, $expire );
|
|
}
|
|
function ssa_cache_delete( $key, $group = '' ) {
|
|
return SSA_Cache::object_cache_delete( $key, $group );
|
|
}
|
|
|
|
|
|
/**
|
|
* SSA custom class_exists function
|
|
*
|
|
* @since 6.0.3
|
|
*
|
|
* @param string $class
|
|
* @return bool
|
|
*/
|
|
function ssa_class_exists( $class = '' ) {
|
|
if ( ! class_exists( $class ) ) {
|
|
$var = 'Class "' . $class . '" not found!';
|
|
$debug_level = 10;
|
|
ssa_debug_log($var, $debug_level );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* SSA custom function_exists function
|
|
*
|
|
* @since 6.0.3
|
|
*
|
|
* @param string $function
|
|
* @return bool
|
|
*/
|
|
function ssa_function_exists( $function = '' ) {
|
|
if ( ! function_exists( $function ) ) {
|
|
$var = 'Function "' . $function . '" not found!';
|
|
$debug_level = 10;
|
|
ssa_debug_log($var, $debug_level );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* SSA custom as_has_scheduled_action that does all the checking needed
|
|
*
|
|
* @since 6.0.3
|
|
*
|
|
* @param string $hook Required
|
|
* @return bool|null
|
|
*/
|
|
function ssa_has_scheduled_action( $hook = '', $args = array() ) {
|
|
|
|
if ( empty( $hook ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_class_exists( 'ActionScheduler' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_function_exists( 'as_has_scheduled_action' ) ) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
return as_has_scheduled_action( $hook, $args );
|
|
|
|
} catch( \Exception $e ) {
|
|
$var = $e->getMessage();
|
|
$debug_level = 10;
|
|
$label = 'Action Scheduler';
|
|
ssa_debug_log( $var, $debug_level, $label );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SSA custom as_schedule_recurring_action that does all the checking needed
|
|
*
|
|
* @since 6.0.3
|
|
*
|
|
* @param integer $timestamp Required
|
|
* @param integer $interval_in_seconds Required
|
|
* @param string $hook Required
|
|
* @param array $args Optional
|
|
* @param string $group Optional
|
|
* @return integer|null The action ID or void
|
|
*/
|
|
function ssa_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook='', $args = array(), $group = '' ) {
|
|
|
|
if ( empty( $timestamp ) || empty( $interval_in_seconds ) || empty( $hook ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_class_exists( 'ActionScheduler' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_function_exists( 'as_schedule_recurring_action' ) ) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// return the ID of the scheduled action
|
|
return as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args, $group );
|
|
|
|
} catch( \Exception $e ) {
|
|
$var = $e->getMessage();
|
|
$debug_level = 10;
|
|
$label = 'Action Scheduler';
|
|
ssa_debug_log( $var, $debug_level, $label );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SSA custom as_unschedule_action that does all the checking needed
|
|
*
|
|
* @since 6.0.3
|
|
*
|
|
* @param string $hook Required
|
|
* @param array $args Optional
|
|
* @param string $group Optional
|
|
* @return integer|null. The scheduled action ID if a scheduled action was found, or null if no matching action found.
|
|
*/
|
|
function ssa_unschedule_action( $hook='', $args = array(), $group = '' ) {
|
|
|
|
if ( empty( $hook ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_class_exists( 'ActionScheduler' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_function_exists( 'as_unschedule_action' ) ) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
return as_unschedule_action( $hook, $args, $group );
|
|
|
|
} catch( \Exception $e ) {
|
|
$var = $e->getMessage();
|
|
$debug_level = 10;
|
|
$label = 'Action Scheduler';
|
|
ssa_debug_log( $var, $debug_level, $label );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SSA custom as_unschedule_all_actions that does all the checking needed
|
|
*
|
|
* @since 6.0.3
|
|
*
|
|
* @param string $hook Required
|
|
* @param array $args Optional
|
|
* @param string $group Optional
|
|
*/
|
|
function ssa_unschedule_all_actions( $hook='', $args = array(), $group = '' ) {
|
|
|
|
if ( empty( $hook ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_class_exists( 'ActionScheduler' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_function_exists( 'as_unschedule_all_actions' ) ) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
return as_unschedule_all_actions( $hook, $args, $group );
|
|
|
|
} catch( \Exception $e ) {
|
|
$var = $e->getMessage();
|
|
$debug_level = 10;
|
|
$label = 'Action Scheduler';
|
|
ssa_debug_log( $var, $debug_level, $label );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SSA custom as_schedule_single_action that does all the checking needed
|
|
*
|
|
* @since 6.0.3
|
|
*
|
|
* @param integer $timestamp Required
|
|
* @param string $hook Required
|
|
* @param array $args Optional
|
|
* @param string $group Optional
|
|
* @return integer|null. The scheduled action ID if a scheduled action was found, or null if no matching action found.
|
|
*/
|
|
function ssa_schedule_single_action( $timestamp, $hook='', $args = array(), $group = '' ) {
|
|
|
|
if ( empty( $timestamp ) || empty( $hook ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_class_exists( 'ActionScheduler' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! ssa_function_exists( 'as_schedule_single_action' ) ) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
return as_schedule_single_action( $timestamp, $hook, $args, $group );
|
|
|
|
} catch( \Exception $e ) {
|
|
$var = $e->getMessage();
|
|
$debug_level = 10;
|
|
$label = 'Action Scheduler';
|
|
ssa_debug_log( $var, $debug_level, $label );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SSA queue runner for wp_actionscheduler_actions table
|
|
*
|
|
* @since 6.4.3
|
|
*
|
|
* @return integer
|
|
*/
|
|
function ssa_run_action_scheduler_queue(){
|
|
|
|
if ( ! class_exists( 'ActionScheduler' ) ) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
return ActionScheduler::runner()->run();
|
|
|
|
} catch( \Exception $e ) {
|
|
$var = $e->getMessage();
|
|
$debug_level = 10;
|
|
$label = 'Action Scheduler';
|
|
ssa_debug_log( $var, $debug_level, $label );
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Get all active plugins
|
|
* Return array of active plugins names, eg. array( 'draw-attention', 'simply-schedule-appointments', 'wp-mail-logging' );
|
|
*
|
|
* @return array
|
|
*/
|
|
function ssa_get_all_active_plugins(){
|
|
|
|
$active_plugins = get_option( 'active_plugins' );
|
|
$output = array();
|
|
|
|
foreach ($active_plugins as $active_plugin ) {
|
|
if ( strpos( $active_plugin, '/' ) ) {
|
|
$active_plugin = substr( $active_plugin, 0, strpos( $active_plugin, '/' ) );
|
|
}
|
|
$output[] = $active_plugin;
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
|
|
function ssa_evaluate_datetime_merge_tag( $start_date, $modifier, $date_timezone ){
|
|
$modifier_string = explode( ':', $modifier, 3);
|
|
// by default format as day of the week
|
|
$formatted_value = ssa_datetime( $start_date )->setTimezone( $date_timezone )->format( 'l' );
|
|
|
|
if( $modifier_string[1] === 'format' ){
|
|
// if a format string is included, format accordingly
|
|
$date_format = str_replace( 'y', 'Y', $modifier_string[2] );
|
|
$date_format = str_replace( 'h', 'H', $date_format );
|
|
$formatted_value = ssa_datetime( $start_date )->setTimezone( $date_timezone )->format( $date_format );
|
|
}
|
|
|
|
$formatted_value = SSA_Utils::translate_formatted_date( $formatted_value );
|
|
return $formatted_value;
|
|
}
|
|
|
|
function ssa_evaluate_merge_tag ( $appointment_id, $modifier ) {
|
|
|
|
try {
|
|
$appointment_obj = new SSA_Appointment_Object( $appointment_id );
|
|
$start_date = $appointment_obj->start_date;
|
|
} catch ( Exception $e ) {
|
|
// failed to get appointment from appointment_id
|
|
return '';
|
|
}
|
|
// get settings
|
|
$settings = ssa()->settings->get();
|
|
|
|
if ( 'business_start_date' === explode( ':', $modifier )[0] ) {
|
|
$date_timezone = SSA_Utils::safe_timezone( $settings['global']['timezone_string'] );
|
|
return ssa_evaluate_datetime_merge_tag( $start_date, $modifier, $date_timezone );
|
|
}
|
|
|
|
if ( 'customer_start_date' === explode( ':', $modifier )[0] && count( explode( ':', $modifier ) ) > 1 ) {
|
|
return ssa_evaluate_datetime_merge_tag( $start_date, $modifier, $appointment_obj->get_customer_timezone() );
|
|
}
|
|
|
|
if ( 'instructions' === $modifier ) {
|
|
return $appointment_obj->get_appointment_type()->instructions;
|
|
}
|
|
|
|
if ( 'public_edit_url' === $modifier ) {
|
|
return $appointment_obj->get_public_edit_url();
|
|
}
|
|
|
|
if ( 'appointment_type_slug' === $modifier ) {
|
|
$appointment_type = $appointment_obj->get_appointment_type();
|
|
return $appointment_type->get_slug();
|
|
}
|
|
|
|
if ( 'appointment_type_title' === $modifier ) {
|
|
$appointment_type = $appointment_obj->get_appointment_type();
|
|
return $appointment_type->get_title();
|
|
}
|
|
|
|
if ( 'web_meeting_url' === $modifier ) {
|
|
return $appointment_obj->__get( 'web_meeting_url' );
|
|
}
|
|
|
|
if ( 'add_to_calendar_link_ics' === $modifier ) {
|
|
$token = ssa()->appointment_model->get_id_token( $appointment_id );
|
|
return add_query_arg(
|
|
array(
|
|
'token' => $token,
|
|
),
|
|
$appointment_obj->get_ics_download_url( 'customer' )
|
|
);
|
|
}
|
|
|
|
if ( 'add_to_calendar_link_gcal' === $modifier ) {
|
|
return $appointment_obj->get_gcal_add_link( 'customer' );
|
|
}
|
|
|
|
if ( 'team_member_name' === $modifier ) {
|
|
$members = $appointment_obj->get_staff_members();
|
|
|
|
if ( empty( $members ) ) {
|
|
return '';
|
|
}
|
|
|
|
// loop through members and get a list of names.
|
|
$names = array();
|
|
foreach ( $members as $member ) {
|
|
$names[] = $member->get_name();
|
|
}
|
|
|
|
if ( count( $names ) > 1 ) {
|
|
$last = array_pop( $names );
|
|
$text = implode( ', ', $names );
|
|
$text .= ' and ' . $last;
|
|
|
|
return $text;
|
|
} else {
|
|
return $names[0];
|
|
}
|
|
}
|
|
|
|
if ( 'team_member_email' === $modifier ) {
|
|
$members = $appointment_obj->get_staff_members();
|
|
|
|
if ( empty( $members ) ) {
|
|
return '';
|
|
}
|
|
|
|
// loop through members and get a list of emails.
|
|
$emails = array();
|
|
foreach ( $members as $member ) {
|
|
$emails[] = $member->get_email();
|
|
}
|
|
|
|
if ( count( $emails ) > 1 ) {
|
|
$last = array_pop( $emails );
|
|
$text = implode( ', ', $emails );
|
|
$text .= ' and ' . $last;
|
|
|
|
return $text;
|
|
} else {
|
|
return $emails[0];
|
|
}
|
|
}
|
|
|
|
$ssa = ssa();
|
|
|
|
if (
|
|
'start_date_only' === $modifier ||
|
|
'start_time_only' === $modifier
|
|
) {
|
|
$business_start_date = ssa_datetime( $start_date );
|
|
|
|
if ( 'start_date_only' === $modifier ) {
|
|
$format = SSA_Utils::get_localized_date_only_format_from_settings();
|
|
$format = SSA_Utils::localize_default_date_strings( $format );
|
|
} else {
|
|
$format = SSA_Utils::get_localized_time_only_format_from_settings();
|
|
$format = SSA_Utils::localize_default_date_strings( $format ) . ' (T)';
|
|
}
|
|
|
|
$formatted_value = $ssa->utils->get_datetime_as_local_datetime( $business_start_date, $appointment_id )->format( $format );
|
|
$formatted_value = SSA_Utils::translate_formatted_date( $formatted_value );
|
|
return $formatted_value;
|
|
}
|
|
|
|
if (
|
|
'customer_start_date' === $modifier ||
|
|
'customer_start_date_only' === $modifier ||
|
|
'customer_start_time_only' === $modifier
|
|
) {
|
|
if ( 'customer_start_date' === $modifier ) {
|
|
$format = SSA_Utils::get_localized_date_format_from_settings();
|
|
$format = SSA_Utils::localize_default_date_strings( $format ) . ' (T)';
|
|
} elseif ( 'customer_start_date_only' === $modifier ) {
|
|
$format = SSA_Utils::get_localized_date_only_format_from_settings();
|
|
$format = SSA_Utils::localize_default_date_strings( $format );
|
|
} elseif ( 'customer_start_time_only' === $modifier ) {
|
|
$format = SSA_Utils::get_localized_time_only_format_from_settings();
|
|
$format = SSA_Utils::localize_default_date_strings( $format ) . ' (T)';
|
|
}
|
|
|
|
$formatted_value = ssa_datetime( $start_date )->setTimezone( $appointment_obj->get_customer_timezone() )->format( $format );
|
|
$formatted_value = SSA_Utils::translate_formatted_date( $formatted_value );
|
|
return $formatted_value;
|
|
}
|
|
|
|
$local_start_date = $ssa->utils->get_datetime_as_local_datetime( $start_date );
|
|
$format = SSA_Utils::get_localized_date_format_from_settings();
|
|
$format = SSA_Utils::localize_default_date_strings( $format ) . ' (T)';
|
|
$formatted_value = $local_start_date->format( $format );
|
|
$formatted_value = SSA_Utils::translate_formatted_date( $formatted_value );
|
|
return $formatted_value;
|
|
}
|
|
|
|
function ssa_get_stack_trace() {
|
|
ob_start();
|
|
debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
|
$trace = ob_get_contents();
|
|
ob_end_clean();
|
|
return $trace;
|
|
}
|
|
|
|
function ssa_should_render_booking_flow() {
|
|
return ssa()->settings_installed->is_installed( 'booking_flows' ) ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Unles the notification is sent to a customer, it's categorized as a staff notification
|
|
*
|
|
* @param array $recipients
|
|
* @return string
|
|
*/
|
|
function ssa_get_recipient_type_for_recipients_array ( array $recipients ) {
|
|
$recipients_string = implode( ',', $recipients );
|
|
if ( strpos( $recipients_string, 'customer_email' ) === false && strpos( $recipients_string, 'customer_phone' ) === false ){
|
|
return 'staff';
|
|
}
|
|
return 'customer';
|
|
}
|
|
|
|
/**
|
|
* Build current page URL without query parameters
|
|
*
|
|
* @return string
|
|
*/
|
|
function ssa_get_current_page_url() {
|
|
global $wp;
|
|
return home_url($wp->request);
|
|
}
|
|
|
|
/**
|
|
* Sanitizes the accent color input.
|
|
*
|
|
* @param string $color The input color string.
|
|
* @return string|null The sanitized color or null if the input is invalid.
|
|
*/
|
|
function ssa_sanitize_color_input($color) {
|
|
$hex_pattern = '/^[a-fA-F0-9]+$/';
|
|
|
|
$rgba_pattern = '/^rgba\(\d{1,3},\d{1,3},\d{1,3},(?:0|1|0?\.\d+)\)$/i';
|
|
|
|
if (preg_match($hex_pattern, $color) && in_array(strlen($color), [6, 3])) {
|
|
return strtolower($color);
|
|
}
|
|
|
|
if (preg_match($rgba_pattern, $color)) {
|
|
return $color;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// sometimes the request hits the php code even though it's a file request
|
|
function ssa_is_json_request() {
|
|
// wp_is_json_request checks for the headers - not helpful here
|
|
// so we check if /wp-json/ is in the request URI
|
|
return strpos( $_SERVER['REQUEST_URI'], '/wp-json/' ) !== false && strpos( $_SERVER['REQUEST_URI'], '/block-renderer/ssa') === false;
|
|
}
|
|
|
|
function ssa_is_file_request() {
|
|
$request_uri = $_SERVER['REQUEST_URI'];
|
|
$file_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'ico', 'css', 'js', 'woff', 'woff2', 'ttf', 'eot', 'otf', 'svg', 'webp', 'map'];
|
|
$path_info = pathinfo($request_uri);
|
|
|
|
if ( isset( $path_info['extension'] ) && in_array( strtolower( $path_info['extension'] ), $file_extensions ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function ssa_should_skip_async_logic() {
|
|
$should_skip = true;
|
|
|
|
if( ssa_doing_async() ) {
|
|
$should_skip = false;
|
|
}
|
|
|
|
// wp cron
|
|
if ( wp_doing_cron() ) {
|
|
$should_skip = false;
|
|
}
|
|
|
|
return $should_skip;
|
|
}
|