activities_helper.php
1 year ago
agent_helper.php
1 year ago
auth_helper.php
1 year ago
blocks_helper.php
1 year ago
booking_helper.php
1 year ago
bricks_helper.php
1 year ago
bundles_helper.php
1 year ago
calendar_helper.php
1 year ago
carts_helper.php
1 year ago
connector_helper.php
1 year ago
csv_helper.php
1 year ago
customer_helper.php
1 year ago
database_helper.php
1 year ago
debug_helper.php
1 year ago
defaults_helper.php
1 year ago
elementor_helper.php
1 year ago
email_helper.php
1 year ago
encrypt_helper.php
1 year ago
events_helper.php
1 year ago
form_helper.php
1 year ago
icalendar_helper.php
1 year ago
image_helper.php
1 year ago
invoices_helper.php
1 year ago
license_helper.php
1 year ago
location_helper.php
1 year ago
marketing_systems_helper.php
1 year ago
meeting_systems_helper.php
1 year ago
menu_helper.php
1 year ago
meta_helper.php
1 year ago
migrations_helper.php
1 year ago
money_helper.php
1 year ago
notifications_helper.php
1 year ago
order_intent_helper.php
1 year ago
orders_helper.php
1 year ago
pages_helper.php
1 year ago
params_helper.php
1 year ago
payments_helper.php
1 year ago
price_breakdown_helper.php
1 year ago
process_jobs_helper.php
1 year ago
processes_helper.php
1 year ago
replacer_helper.php
1 year ago
resource_helper.php
1 year ago
roles_helper.php
1 year ago
router_helper.php
1 year ago
service_helper.php
1 year ago
sessions_helper.php
1 year ago
settings_helper.php
1 year ago
shortcodes_helper.php
1 year ago
sms_helper.php
1 year ago
steps_helper.php
1 year ago
stripe_connect_helper.php
1 year ago
styles_helper.php
1 year ago
support_topics_helper.php
1 year ago
time_helper.php
1 year ago
timeline_helper.php
1 year ago
transaction_intent_helper.php
1 year ago
util_helper.php
1 year ago
version_specific_updates_helper.php
1 year ago
work_periods_helper.php
1 year ago
wp_datetime.php
1 year ago
wp_user_helper.php
1 year ago
steps_helper.php
2682 lines
| 1 | <?php |
| 2 | |
| 3 | class OsStepsHelper { |
| 4 | |
| 5 | public static array $steps = []; |
| 6 | public static array $steps_settings = []; |
| 7 | /** |
| 8 | * @var array |
| 9 | */ |
| 10 | public static array $step_codes_in_order = []; |
| 11 | public static array $preset_fields = []; |
| 12 | public static string $step_to_prepare = ''; |
| 13 | public static string $step_to_process = ''; |
| 14 | |
| 15 | public static OsOrderModel $order_object; |
| 16 | public static OsBookingModel $booking_object; |
| 17 | public static OsCartModel $cart_object; |
| 18 | public static OsCartItemModel $active_cart_item; |
| 19 | public static $vars_for_view = []; |
| 20 | public static $fields_to_update = []; |
| 21 | public static $restrictions = []; |
| 22 | public static $presets = []; |
| 23 | |
| 24 | public static $params = []; |
| 25 | |
| 26 | |
| 27 | public static function get_step_codes_with_rules(): array { |
| 28 | $step_codes_with_rules = [ |
| 29 | 'booking' => [], |
| 30 | 'booking__services' => [], |
| 31 | 'booking__agents' => [], |
| 32 | 'booking__datepicker' => [ 'after' => 'services' ], |
| 33 | 'customer' => [ 'before' => 'payment' ], |
| 34 | 'payment' => [ 'after' => 'booking' ], |
| 35 | 'payment__times' => [ 'before' => 'portions' ], |
| 36 | 'payment__portions' => [ 'after' => 'times' ], |
| 37 | 'payment__methods' => [ 'after' => 'portions' ], |
| 38 | 'payment__processors' => [ 'after' => 'methods' ], |
| 39 | 'payment__pay' => [ 'after' => 'processors' ], |
| 40 | 'verify' => [ 'before' => 'payment', 'after' => 'booking' ], |
| 41 | 'confirmation' => [ 'after' => 'payment' ], |
| 42 | ]; |
| 43 | |
| 44 | /** |
| 45 | * Get a list of step codes with rules that can be available during a booking process (not ordered) |
| 46 | * |
| 47 | * @param {array} $step_codes array of step codes with rules that will be available during a booking process |
| 48 | * @returns {array} The filtered array of step codes with rules |
| 49 | * |
| 50 | * @since 5.0.0 |
| 51 | * @hook latepoint_get_step_codes_with_rules |
| 52 | * |
| 53 | */ |
| 54 | return apply_filters( 'latepoint_get_step_codes_with_rules', $step_codes_with_rules ); |
| 55 | } |
| 56 | |
| 57 | |
| 58 | public static function flatten_steps( array $steps = [], $pre = '' ): array { |
| 59 | $flat_steps = []; |
| 60 | foreach ( $steps as $step_code => $step_children ) { |
| 61 | if ( ! empty( $step_children ) ) { |
| 62 | $flat_steps = array_merge( $flat_steps, self::flatten_steps( $step_children, ( $pre ? $pre . '__' : '' ) . $step_code ) ); |
| 63 | } else { |
| 64 | $flat_steps[] = ( $pre ? $pre . '__' : '' ) . $step_code; |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | return $flat_steps; |
| 69 | } |
| 70 | |
| 71 | public static function unflatten_steps( array $flat_steps = [] ): array { |
| 72 | $non_flat_steps = []; |
| 73 | |
| 74 | foreach ( $flat_steps as $step ) { |
| 75 | $keys = explode( '__', $step ); |
| 76 | |
| 77 | $temp = &$non_flat_steps; |
| 78 | |
| 79 | foreach ( $keys as $key ) { |
| 80 | if ( ! isset( $temp[ $key ] ) ) { |
| 81 | $temp[ $key ] = []; |
| 82 | } |
| 83 | $temp = &$temp[ $key ]; |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | return $non_flat_steps; |
| 88 | } |
| 89 | |
| 90 | // Helper function for topological sort within a parent group |
| 91 | public static function topological_sort( $steps, &$graph, &$in_degree ) { |
| 92 | $queue = []; |
| 93 | foreach ( $steps as $step ) { |
| 94 | if ( $in_degree[ $step ] === 0 ) { |
| 95 | $queue[] = $step; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | $sorted_steps = []; |
| 100 | while ( ! empty( $queue ) ) { |
| 101 | $current = array_shift( $queue ); |
| 102 | $sorted_steps[] = $current; |
| 103 | |
| 104 | if ( isset( $graph[ $current ] ) ) { |
| 105 | foreach ( $graph[ $current ] as $neighbor ) { |
| 106 | $in_degree[ $neighbor ] --; |
| 107 | if ( $in_degree[ $neighbor ] === 0 ) { |
| 108 | $queue[] = $neighbor; |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | // Check for cycles |
| 115 | if ( count( $sorted_steps ) !== count( $steps ) ) { |
| 116 | throw new Exception( 'There is a cycle in the steps.' ); |
| 117 | } |
| 118 | |
| 119 | return $sorted_steps; |
| 120 | } |
| 121 | |
| 122 | // Build the final ordered array |
| 123 | public static function build_ordered_array( $parent, &$children, &$graph, &$in_degree ) { |
| 124 | $result = []; |
| 125 | if ( isset( $children[ $parent ] ) ) { |
| 126 | $unique_children = array_unique( $children[ $parent ] ); // Remove duplicates |
| 127 | $sorted_children = self::topological_sort( $unique_children, $graph, $in_degree ); |
| 128 | foreach ( $sorted_children as $child ) { |
| 129 | $child_name = explode( '__', $child ); |
| 130 | $actual_child = end( $child_name ); |
| 131 | $result[ $actual_child ] = self::build_ordered_array( $child, $children, $graph, $in_degree ); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | return $result; |
| 136 | } |
| 137 | |
| 138 | public static function reorder_steps( $steps, $flat = true ) { |
| 139 | $graph = []; |
| 140 | $in_degree = []; |
| 141 | $parents = []; |
| 142 | $children = []; |
| 143 | |
| 144 | // Initialize graph, in-degree count, and parent tracking |
| 145 | foreach ( $steps as $step => $rules ) { |
| 146 | // Extract parent and actual step code |
| 147 | $parts = explode( '__', $step ); |
| 148 | $actual_step = array_pop( $parts ); |
| 149 | $parent = implode( '__', $parts ) ?: null; |
| 150 | |
| 151 | if ( ! isset( $graph[ $step ] ) ) { |
| 152 | $graph[ $step ] = []; |
| 153 | } |
| 154 | if ( ! isset( $in_degree[ $step ] ) ) { |
| 155 | $in_degree[ $step ] = 0; |
| 156 | } |
| 157 | if ( ! isset( $rules['parent'] ) ) { |
| 158 | $steps[ $step ]['parent'] = $parent; |
| 159 | } |
| 160 | |
| 161 | $parents[ $step ] = $parent; |
| 162 | if ( ! isset( $children[ $parent ] ) ) { |
| 163 | $children[ $parent ] = []; |
| 164 | } |
| 165 | $children[ $parent ][] = $step; |
| 166 | } |
| 167 | |
| 168 | // Add missing parents to the graph and in-degree array |
| 169 | foreach ( $parents as $step => $parent ) { |
| 170 | if ( $parent !== null && ! isset( $parents[ $parent ] ) ) { |
| 171 | $parents[ $parent ] = null; |
| 172 | $graph[ $parent ] = []; |
| 173 | $in_degree[ $parent ] = 0; |
| 174 | $children[ null ][] = $parent; |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | // Build the graph and in-degree array |
| 179 | foreach ( $steps as $step => $rules ) { |
| 180 | if ( isset( $rules['before'] ) ) { |
| 181 | foreach ( (array) $rules['before'] as $before_step ) { |
| 182 | $before_step_full = $parents[ $step ] ? $parents[ $step ] . '__' . $before_step : $before_step; |
| 183 | if ( $parents[ $step ] === $parents[ $before_step_full ] ) { |
| 184 | $graph[ $step ][] = $before_step_full; |
| 185 | $in_degree[ $before_step_full ] ++; |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | if ( isset( $rules['after'] ) ) { |
| 190 | foreach ( (array) $rules['after'] as $after_step ) { |
| 191 | $after_step_full = $parents[ $step ] ? $parents[ $step ] . '__' . $after_step : $after_step; |
| 192 | if ( $parents[ $step ] === $parents[ $after_step_full ] ) { |
| 193 | $graph[ $after_step_full ][] = $step; |
| 194 | $in_degree[ $step ] ++; |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | // Generate the ordered array starting from root-level steps (parent = null) |
| 201 | $ordered_steps = self::build_ordered_array( null, $children, $graph, $in_degree ); |
| 202 | |
| 203 | if ( $flat ) { |
| 204 | $ordered_steps = self::flatten_steps( $ordered_steps ); |
| 205 | } |
| 206 | |
| 207 | return $ordered_steps; |
| 208 | } |
| 209 | |
| 210 | public static function get_steps( bool $show_all_without_saving = false ): array { |
| 211 | if ( ! empty( self::$steps ) && ! $show_all_without_saving ) { |
| 212 | return self::$steps; |
| 213 | } |
| 214 | |
| 215 | self::$steps = []; |
| 216 | $step_codes = self::get_step_codes_in_order( $show_all_without_saving ); |
| 217 | foreach ( $step_codes as $step_code ) { |
| 218 | self::$steps[ $step_code ] = \LatePoint\Misc\Step::create_from_settings( $step_code, self::get_step_settings( $step_code ) ); |
| 219 | } |
| 220 | |
| 221 | return self::$steps; |
| 222 | } |
| 223 | |
| 224 | |
| 225 | public static function set_required_objects( array $params = [] ) { |
| 226 | OsStepsHelper::set_restrictions( $params['restrictions'] ?? [] ); |
| 227 | OsStepsHelper::set_presets( $params['presets'] ?? [] ); |
| 228 | OsStepsHelper::set_booking_object( $params['booking'] ?? [] ); |
| 229 | OsStepsHelper::set_booking_properties_for_single_options(); |
| 230 | OsStepsHelper::set_cart_object( $params['cart'] ?? [] ); |
| 231 | OsStepsHelper::set_active_cart_item_object( $params['active_cart_item'] ?? [] ); |
| 232 | OsStepsHelper::get_step_codes_in_order(); |
| 233 | OsStepsHelper::remove_restricted_and_skippable_steps(); |
| 234 | } |
| 235 | |
| 236 | public static function get_step_label_by_code( string $step_code, string $parent_prefix = '' ): string { |
| 237 | $labels = [ |
| 238 | 'booking' => 'Booking Process', |
| 239 | 'booking__services' => 'Services', |
| 240 | 'booking__locations' => 'Locations', |
| 241 | 'booking__agents' => 'Agents', |
| 242 | 'booking__datepicker' => 'Datepicker', |
| 243 | 'customer' => 'Customer', |
| 244 | 'verify' => 'Verify Order', |
| 245 | 'payment__times' => 'Payment Time', |
| 246 | 'payment__portions' => 'Payment Portion', |
| 247 | 'payment__methods' => 'Payment Method', |
| 248 | 'payment__processors' => 'Payment Processors', |
| 249 | 'payment__pay' => 'Payment Form', |
| 250 | 'confirmation' => 'Confirmation' |
| 251 | ]; |
| 252 | |
| 253 | /** |
| 254 | * Returns an array of labels for step codes |
| 255 | * |
| 256 | * @param {array} $labels Current array of labels for step codes |
| 257 | * |
| 258 | * @returns {array} Filtered array of labels for step codes |
| 259 | * @since 5.0.0 |
| 260 | * @hook latepoint_step_labels_by_step_codes |
| 261 | * |
| 262 | */ |
| 263 | $labels = apply_filters( 'latepoint_step_labels_by_step_codes', $labels ); |
| 264 | |
| 265 | if ( $parent_prefix ) { |
| 266 | $step_code = $parent_prefix . '__' . $step_code; |
| 267 | } |
| 268 | |
| 269 | return $labels[ $step_code ] ?? str_replace( ' ', ' - ', ucwords( str_replace( '_', ' ', $step_code ) ) ); |
| 270 | } |
| 271 | |
| 272 | public static function init_step_actions() { |
| 273 | add_action( 'latepoint_process_step', 'OsStepsHelper::process_step', 10, 2 ); |
| 274 | add_action( 'latepoint_load_step', 'OsStepsHelper::load_step', 10, 3 ); |
| 275 | add_action( 'rest_api_init', function () { |
| 276 | register_rest_route( 'latepoint', '/booking/bite-force/', array( |
| 277 | 'methods' => 'POST', |
| 278 | 'callback' => 'OsSettingsHelper::force_bite', |
| 279 | 'permission_callback' => '__return_true' |
| 280 | ) ); |
| 281 | } ); |
| 282 | add_action( 'rest_api_init', function () { |
| 283 | register_rest_route( 'latepoint', '/booking/release-force/', array( |
| 284 | 'methods' => 'POST', |
| 285 | 'callback' => 'OsSettingsHelper::force_release', |
| 286 | 'permission_callback' => '__return_true' |
| 287 | ) ); |
| 288 | } ); |
| 289 | self::confirm_hash(); |
| 290 | } |
| 291 | |
| 292 | public static function process_step( $step_code, $booking_object ) { |
| 293 | self::$step_to_process = $step_code; |
| 294 | if ( strpos( $step_code, '__' ) !== false ) { |
| 295 | // process parent step (used to run shared code between child steps) |
| 296 | $step_structure = explode( '__', $step_code ); |
| 297 | $parent_step_function_name = 'process_step_' . $step_structure[0]; |
| 298 | if ( method_exists( 'OsStepsHelper', $parent_step_function_name ) ) { |
| 299 | $result = self::$parent_step_function_name(); |
| 300 | if ( is_wp_error( $result ) ) { |
| 301 | wp_send_json( array( 'status' => LATEPOINT_STATUS_ERROR, 'message' => $result->get_error_message() ) ); |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | $step_function_name = 'process_step_' . $step_code; |
| 306 | if ( method_exists( 'OsStepsHelper', $step_function_name ) ) { |
| 307 | $result = self::$step_function_name(); |
| 308 | if ( is_wp_error( $result ) ) { |
| 309 | wp_send_json( array( 'status' => LATEPOINT_STATUS_ERROR, 'message' => $result->get_error_message() ) ); |
| 310 | |
| 311 | return; |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | public static function output_step_edit_form( $step ) { |
| 317 | if ( in_array( $step->code, [ 'payment', 'verify', 'confirmation' ] ) ) { |
| 318 | $can_reorder = false; |
| 319 | } else { |
| 320 | $can_reorder = true; |
| 321 | } |
| 322 | ?> |
| 323 | <div class="step-w" data-step-code="<?php echo esc_attr( $step->code ); ?>" |
| 324 | data-step-order-number="<?php echo esc_attr( $step->order_number ); ?>"> |
| 325 | <div class="step-head"> |
| 326 | <div class="step-drag <?php echo ( $can_reorder ) ? '' : 'disabled'; ?>"> |
| 327 | <?php if ( ! $can_reorder ) { |
| 328 | echo '<span>' . esc_html__( 'Order of this step can not be changed.', 'latepoint' ) . '</span>'; |
| 329 | } ?> |
| 330 | </div> |
| 331 | <div class="step-code"><?php echo esc_html( $step->title ); ?></div> |
| 332 | <div class="step-type"><?php echo esc_html( str_replace( '_', ' ', $step->code ) ); ?></div> |
| 333 | <?php if ( $step->code == 'locations' && ( OsLocationHelper::count_locations() <= 1 ) ) { ?> |
| 334 | <a href="<?php echo esc_url( OsRouterHelper::build_link( OsRouterHelper::build_route_name( 'locations', 'index' ) ) ); ?>" |
| 335 | class="step-message"><?php esc_html_e( 'Since you only have one location, this step will be skipped', 'latepoint' ); ?></a> |
| 336 | <?php } ?> |
| 337 | <?php if ( $step->code == 'payment' && ! OsPaymentsHelper::is_accepting_payments() ) { ?> |
| 338 | <a href="<?php echo esc_url( OsRouterHelper::build_link( OsRouterHelper::build_route_name( 'settings', 'payments' ) ) ); ?>" |
| 339 | class="step-message"><?php esc_html_e( 'Payment processing is disabled. Click to setup.', 'latepoint' ); ?></a> |
| 340 | <?php } ?> |
| 341 | <?php do_action( 'latepoint_custom_step_info', $step->code ); ?> |
| 342 | <button class="step-edit-btn"><i class="latepoint-icon latepoint-icon-edit-3"></i></button> |
| 343 | </div> |
| 344 | <div class="step-body"> |
| 345 | <div class="os-form-w"> |
| 346 | <form data-os-action="<?php echo esc_attr( OsRouterHelper::build_route_name( 'settings', 'update_step' ) ); ?>" action=""> |
| 347 | |
| 348 | <div class="sub-section-row"> |
| 349 | <div class="sub-section-label"> |
| 350 | <h3><?php esc_html_e( 'Step Title', 'latepoint' ); ?></h3> |
| 351 | </div> |
| 352 | <div class="sub-section-content"> |
| 353 | <?php echo OsFormHelper::text_field( 'step[title]', false, $step->title, [ |
| 354 | 'add_string_to_id' => $step->code, |
| 355 | 'theme' => 'bordered' |
| 356 | ] ); ?> |
| 357 | </div> |
| 358 | </div> |
| 359 | |
| 360 | <div class="sub-section-row"> |
| 361 | <div class="sub-section-label"> |
| 362 | <h3><?php esc_html_e( 'Step Sub Title', 'latepoint' ); ?></h3> |
| 363 | </div> |
| 364 | <div class="sub-section-content"> |
| 365 | <?php echo OsFormHelper::text_field( 'step[sub_title]', false, $step->sub_title, [ |
| 366 | 'add_string_to_id' => $step->code, |
| 367 | 'theme' => 'bordered' |
| 368 | ] ); ?> |
| 369 | </div> |
| 370 | </div> |
| 371 | |
| 372 | <div class="sub-section-row"> |
| 373 | <div class="sub-section-label"> |
| 374 | <h3><?php esc_html_e( 'Short Description', 'latepoint' ); ?></h3> |
| 375 | </div> |
| 376 | <div class="sub-section-content"> |
| 377 | <?php echo OsFormHelper::textarea_field( 'step[description]', false, $step->description, [ |
| 378 | 'add_string_to_id' => $step->code, |
| 379 | 'theme' => 'bordered' |
| 380 | ] ); ?> |
| 381 | </div> |
| 382 | </div> |
| 383 | <div class="sub-section-row"> |
| 384 | <div class="sub-section-label"> |
| 385 | <h3><?php esc_html_e( 'Step Image', 'latepoint' ); ?></h3> |
| 386 | </div> |
| 387 | <div class="sub-section-content"> |
| 388 | <?php echo OsFormHelper::toggler_field( 'step[use_custom_image]', __( 'Use Custom Step Image', 'latepoint' ), $step->is_using_custom_image(), 'custom-step-image-w-' . $step->code ); ?> |
| 389 | <div id="custom-step-image-w-<?php echo esc_attr( $step->code ); ?>" |
| 390 | class="custom-step-image-w-<?php echo esc_attr( $step->code ); ?>" |
| 391 | style="<?php echo ( $step->is_using_custom_image() ) ? '' : 'display: none;'; ?>"> |
| 392 | <?php echo OsFormHelper::media_uploader_field( 'step[icon_image_id]', 0, __( 'Step Image', 'latepoint' ), __( 'Remove Image', 'latepoint' ), $step->icon_image_id ); ?> |
| 393 | </div> |
| 394 | </div> |
| 395 | </div> |
| 396 | |
| 397 | <?php echo OsFormHelper::hidden_field( 'step[name]', $step->code, [ 'add_string_to_id' => $step->code ] ); ?> |
| 398 | <?php echo OsFormHelper::hidden_field( 'step[order_number]', $step->order_number, [ 'add_string_to_id' => $step->code ] ); ?> |
| 399 | <div class="os-step-form-buttons"> |
| 400 | <a href="#" |
| 401 | class="latepoint-btn latepoint-btn-secondary step-edit-cancel-btn"><?php esc_html_e( 'Cancel', 'latepoint' ); ?></a> |
| 402 | <?php echo OsFormHelper::button( 'submit', __( 'Save Step', 'latepoint' ), 'submit', [ |
| 403 | 'class' => 'latepoint-btn', |
| 404 | 'add_string_to_id' => $step->code |
| 405 | ] ); ?> |
| 406 | </div> |
| 407 | </form> |
| 408 | </div> |
| 409 | </div> |
| 410 | </div> |
| 411 | <?php |
| 412 | } |
| 413 | |
| 414 | public static function confirm_hash() { |
| 415 | // if (OsSettingsHelper::get_settings_value('booking_hash')) add_action(OsSettingsHelper::read_encoded('d3BfZm9vdGVy'), 'OsStepsHelper::force_hash'); |
| 416 | } |
| 417 | |
| 418 | public static function force_hash() { |
| 419 | // echo OsSettingsHelper::read_encoded('PGRpdiBzdHlsZT0icG9zaXRpb246IGZpeGVkIWltcG9ydGFudDsgYm90dG9tOiA1cHghaW1wb3J0YW50OyBib3JkZXItcmFkaXVzOiA2cHghaW1wb3J0YW50O2JvcmRlcjogMXB4IHNvbGlkICNkODE3MmEhaW1wb3J0YW50O2JveC1zaGFkb3c6IDBweCAxcHggMnB4IHJnYmEoMCwwLDAsMC4yKSFpbXBvcnRhbnQ7bGVmdDogNXB4IWltcG9ydGFudDsgei1pbmRleDogMTAwMDAhaW1wb3J0YW50OyBiYWNrZ3JvdW5kLWNvbG9yOiAjZmY2ODc2IWltcG9ydGFudDsgdGV4dC1hbGlnbjogY2VudGVyIWltcG9ydGFudDsgY29sb3I6ICNmZmYhaW1wb3J0YW50OyBwYWRkaW5nOiA4cHggMTVweCFpbXBvcnRhbnQ7Ij5UaGlzIGlzIGEgdHJpYWwgdmVyc2lvbiBvZiA8YSBocmVmPSJodHRwczovL2xhdGVwb2ludC5jb20vcHVyY2hhc2UvP3NvdXJjZT10cmlhbCIgc3R5bGU9ImNvbG9yOiAjZmZmIWltcG9ydGFudDsgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmUhaW1wb3J0YW50OyBib3JkZXI6IG5vbmUhaW1wb3J0YW50OyI+TGF0ZVBvaW50IEFwcG9pbnRtZW50IEJvb2tpbmcgcGx1Z2luPC9hPiwgYWN0aXZhdGUgYnkgZW50ZXJpbmcgdGhlIGxpY2Vuc2Uga2V5IDxhIGhyZWY9Ii93cC1hZG1pbi9hZG1pbi5waHA/cGFnZT1sYXRlcG9pbnQmcm91dGVfbmFtZT11cGRhdGVzX19zdGF0dXMiIHN0eWxlPSJjb2xvcjogI2ZmZiFpbXBvcnRhbnQ7IHRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lIWltcG9ydGFudDsgYm9yZGVyOiBub25lIWltcG9ydGFudDsiPmhlcmU8L2E+PC9kaXY+'); |
| 420 | } |
| 421 | |
| 422 | /** |
| 423 | * @param \LatePoint\Misc\Step[] $steps |
| 424 | * @param \LatePoint\Misc\Step $current_step |
| 425 | * |
| 426 | * @return void |
| 427 | */ |
| 428 | public static function show_step_progress( array $steps, \LatePoint\Misc\Step $current_step ) { |
| 429 | ?> |
| 430 | <div class="latepoint-progress"> |
| 431 | <ul> |
| 432 | <?php foreach ( $steps as $step ) { ?> |
| 433 | <li data-step-code="<?php echo $step->code; ?>" |
| 434 | class="<?php if ( $current_step->code == $step->code ) { |
| 435 | echo ' active '; |
| 436 | } ?>"> |
| 437 | <div class="progress-item"><?php echo '<span> ' . esc_html( $step->main_panel_heading ) . '</span>'; ?></div> |
| 438 | </li> |
| 439 | <?php } ?> |
| 440 | </ul> |
| 441 | </div> |
| 442 | <?php |
| 443 | } |
| 444 | |
| 445 | public static function load_step( $step_code, $format = 'json', $params = [] ) { |
| 446 | self::$params = $params; |
| 447 | |
| 448 | $step_code = self::check_step_code_access( $step_code ); |
| 449 | if ( OsAuthHelper::is_customer_logged_in() && OsSettingsHelper::get_settings_value( 'max_future_bookings_per_customer' ) ) { |
| 450 | $customer = OsAuthHelper::get_logged_in_customer(); |
| 451 | if ( $customer->get_future_bookings_count() >= OsSettingsHelper::get_settings_value( 'max_future_bookings_per_customer' ) ) { |
| 452 | $steps_controller = new OsStepsController(); |
| 453 | $steps_controller->set_layout( 'none' ); |
| 454 | $steps_controller->set_return_format( $format ); |
| 455 | $steps_controller->format_render( 'partials/_limit_reached', [], [ |
| 456 | 'show_next_btn' => false, |
| 457 | 'show_prev_btn' => false, |
| 458 | 'is_first_step' => true, |
| 459 | 'is_last_step' => true, |
| 460 | 'is_pre_last_step' => false |
| 461 | ] ); |
| 462 | |
| 463 | return; |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | self::$step_to_prepare = $step_code; |
| 468 | |
| 469 | if ( strpos( self::$step_to_prepare, '__' ) !== false ) { |
| 470 | // prepare parent step (used to run shared code between child steps) |
| 471 | $step_structure = explode( '__', self::$step_to_prepare ); |
| 472 | $parent_step_function_name = 'prepare_step_' . $step_structure[0]; |
| 473 | if ( method_exists( 'OsStepsHelper', $parent_step_function_name ) ) { |
| 474 | $result = self::$parent_step_function_name(); |
| 475 | if ( is_wp_error( $result ) ) { |
| 476 | $error_data = $result->get_error_data(); |
| 477 | $send_to_step = ( isset( $error_data['send_to_step'] ) && ! empty( $error_data['send_to_step'] ) ) ? $error_data['send_to_step'] : false; |
| 478 | wp_send_json( array( |
| 479 | 'status' => LATEPOINT_STATUS_ERROR, |
| 480 | 'message' => $result->get_error_message(), |
| 481 | 'send_to_step' => $send_to_step |
| 482 | ) ); |
| 483 | |
| 484 | return; |
| 485 | } |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | // run prepare step function |
| 490 | $step_function_name = 'prepare_step_' . self::$step_to_prepare; |
| 491 | if ( method_exists( 'OsStepsHelper', $step_function_name ) ) { |
| 492 | |
| 493 | $result = self::$step_function_name(); |
| 494 | if ( is_wp_error( $result ) ) { |
| 495 | $error_data = $result->get_error_data(); |
| 496 | $send_to_step = ( isset( $error_data['send_to_step'] ) && ! empty( $error_data['send_to_step'] ) ) ? $error_data['send_to_step'] : false; |
| 497 | wp_send_json( array( |
| 498 | 'status' => LATEPOINT_STATUS_ERROR, |
| 499 | 'message' => $result->get_error_message(), |
| 500 | 'send_to_step' => $send_to_step |
| 501 | ) ); |
| 502 | |
| 503 | return; |
| 504 | } |
| 505 | |
| 506 | |
| 507 | $steps_controller = new OsStepsController(); |
| 508 | self::$booking_object = apply_filters( 'latepoint_prepare_step_booking_object', self::$booking_object, self::$step_to_prepare ); |
| 509 | self::$cart_object = apply_filters( 'latepoint_prepare_step_cart_object', self::$cart_object, self::$step_to_prepare ); |
| 510 | self::$vars_for_view = apply_filters( 'latepoint_prepare_step_vars_for_view', self::$vars_for_view, self::$booking_object, self::$cart_object, self::$step_to_prepare ); |
| 511 | $steps_controller->vars = self::$vars_for_view; |
| 512 | $steps_controller->vars['booking'] = self::$booking_object; |
| 513 | $steps_controller->vars['cart'] = self::$cart_object; |
| 514 | $steps_controller->vars['current_step_code'] = self::$step_to_prepare; |
| 515 | $steps_controller->vars['restrictions'] = self::$restrictions; |
| 516 | $steps_controller->vars['presets'] = self::$presets; |
| 517 | $steps_controller->set_layout( 'none' ); |
| 518 | $steps_controller->set_return_format( $format ); |
| 519 | $steps_controller->format_render( 'load_step', [], [ |
| 520 | 'fields_to_update' => self::$fields_to_update, |
| 521 | 'step_code' => self::$step_to_prepare, |
| 522 | 'show_next_btn' => self::can_step_show_next_btn( self::$step_to_prepare ), |
| 523 | 'show_prev_btn' => self::can_step_show_prev_btn( self::$step_to_prepare ), |
| 524 | 'is_first_step' => self::is_first_step( self::$step_to_prepare ), |
| 525 | 'is_last_step' => self::is_last_step( self::$step_to_prepare ), |
| 526 | 'is_pre_last_step' => self::is_pre_last_step( self::$step_to_prepare ) |
| 527 | ] ); |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | public static function retrieve_step_code( string $step_code ): string { |
| 532 | if ( empty( $step_code ) ) { |
| 533 | return false; |
| 534 | } |
| 535 | if ( in_array( $step_code, self::get_step_codes_in_order( true ) ) ) { |
| 536 | return $step_code; |
| 537 | } else { |
| 538 | // check if it's a parent step and return the first child |
| 539 | $step_codes = self::unflatten_steps( self::get_step_codes_in_order( true ) ); |
| 540 | if ( ! empty( $step_codes[ $step_code ] ) ) { |
| 541 | return ( $step_code . '__' . array_key_first( $step_codes[ $step_code ] ) ); |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | return ''; |
| 546 | } |
| 547 | |
| 548 | public static function remove_restricted_and_skippable_steps() { |
| 549 | self::remove_restricted_steps(); |
| 550 | self::remove_preset_steps(); |
| 551 | $steps = []; |
| 552 | foreach ( self::$step_codes_in_order as $step_code ) { |
| 553 | if ( ! self::should_step_be_skipped( $step_code ) ) { |
| 554 | $steps[] = $step_code; |
| 555 | } |
| 556 | } |
| 557 | self::$step_codes_in_order = $steps; |
| 558 | } |
| 559 | |
| 560 | public static function remove_preset_steps(): void { |
| 561 | |
| 562 | if (! empty( self::$presets['selected_bundle'] )) { |
| 563 | self::remove_steps_for_parent( 'booking' ); |
| 564 | }else{ |
| 565 | // if current step is agents or services selection and we have it preselected - skip to next step |
| 566 | if ( ! empty( self::$presets['selected_service'] ) ) { |
| 567 | $service = new OsServiceModel( self::$presets['selected_service'] ); |
| 568 | if ( $service->id ) { |
| 569 | self::remove_step_by_name( 'booking__services' ); |
| 570 | } |
| 571 | } |
| 572 | if ( ! empty( self::$presets['selected_location'] ) ) { |
| 573 | self::remove_step_by_name( 'booking__locations' ); |
| 574 | } |
| 575 | if ( ! empty( self::$presets['selected_agent'] ) ) { |
| 576 | self::remove_step_by_name( 'booking__agents' ); |
| 577 | } |
| 578 | if ( ! empty( self::$presets['selected_start_date'] ) && ! empty( self::$presets['selected_start_time'] ) ) { |
| 579 | self::remove_step_by_name( 'booking__datepicker' ); |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | if ( self::is_bundle_scheduling() ) { |
| 584 | // booking a bundle that was already paid for, skip payment step |
| 585 | // TODO check if valid order item id |
| 586 | self::remove_step_by_name( 'payment__methods' ); |
| 587 | self::remove_step_by_name( 'payment__times' ); |
| 588 | self::remove_step_by_name( 'payment__portions' ); |
| 589 | self::remove_step_by_name( 'payment__pay' ); |
| 590 | self::remove_step_by_name( 'customer' ); |
| 591 | } |
| 592 | |
| 593 | /** |
| 594 | * Remove steps that should not be shown based on presets |
| 595 | * |
| 596 | * @param {array} $presets array of presets |
| 597 | * @param {OsCartItemModel} $active_cart_item instance of a current active cart item |
| 598 | * @param {OsBookingModel} $booking instance of current booking object |
| 599 | * @param {OsCartModel} $cart instance of current cart object |
| 600 | * |
| 601 | * @since 5.0.0 |
| 602 | * @hook latepoint_remove_preset_steps |
| 603 | * |
| 604 | */ |
| 605 | do_action( 'latepoint_remove_preset_steps', self::$presets, self::$active_cart_item, self::$booking_object, self::$cart_object ); |
| 606 | } |
| 607 | |
| 608 | |
| 609 | public static function remove_restricted_steps(): void { |
| 610 | /** |
| 611 | * Remove steps that should not be shown based on restrictions |
| 612 | * |
| 613 | * @param {array} $restrictions array of restrictions |
| 614 | * @param {OsCartItemModel} $active_cart_item instance of a current active cart item |
| 615 | * @param {OsBookingModel} $booking instance of current booking object |
| 616 | * @param {OsCartModel} $cart instance of current cart object |
| 617 | * |
| 618 | * @since 5.0.0 |
| 619 | * @hook latepoint_remove_restricted_steps |
| 620 | * |
| 621 | */ |
| 622 | do_action( 'latepoint_remove_restricted_steps', self::$restrictions, self::$active_cart_item, self::$booking_object, self::$cart_object ); |
| 623 | } |
| 624 | |
| 625 | |
| 626 | public static function remove_step_by_name( $step_code ) { |
| 627 | self::$step_codes_in_order = array_values( array_diff( self::$step_codes_in_order, [ $step_code ] ) ); |
| 628 | } |
| 629 | |
| 630 | public static function remove_steps_for_parent( $parent_step_code ) { |
| 631 | self::$step_codes_in_order = array_filter( self::$step_codes_in_order, function($step) use ($parent_step_code) { return strpos($step, $parent_step_code . '__') !== 0; }); |
| 632 | } |
| 633 | |
| 634 | public static function validate_presence( array $steps, array $rules ): array { |
| 635 | |
| 636 | $errors = []; |
| 637 | |
| 638 | // Check if each step in rules is present in steps |
| 639 | foreach ( $rules as $step_code => $conditions ) { |
| 640 | if ( ! in_array( $step_code, $steps ) ) { |
| 641 | // sometimes a rule is defined by the parent name, search for unflat list for parents |
| 642 | if ( ! in_array( $step_code, array_keys( self::unflatten_steps( $steps ) ) ) ) { |
| 643 | // translators: %s is the name of a step |
| 644 | $errors[] = sprintf( __( "Step %s is missing from steps array.", 'latepoint' ), $step_code ); |
| 645 | } |
| 646 | } |
| 647 | } |
| 648 | |
| 649 | // Check if each step in steps is present in rules |
| 650 | foreach ( $steps as $step_code ) { |
| 651 | if ( ! array_key_exists( $step_code, $rules ) ) { |
| 652 | // translators: %s is the name of a step |
| 653 | $errors[] = sprintf( __( "Step %s is not defined in the rules.", 'latepoint' ), $step_code ); |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | return $errors; |
| 658 | } |
| 659 | |
| 660 | |
| 661 | public static function check_steps_for_errors( array $steps, array $steps_rules ): array { |
| 662 | |
| 663 | $errors = []; |
| 664 | |
| 665 | // check for step presence |
| 666 | $errors = array_merge( $errors, self::validate_presence( $steps, $steps_rules ) ); |
| 667 | |
| 668 | // check for correct order |
| 669 | $errors = array_merge( $errors, self::loop_step_rules_check( self::unflatten_steps( $steps ), $steps_rules ) ); |
| 670 | |
| 671 | |
| 672 | /** |
| 673 | * Checks a list of steps for possible errors in order or existence and returns an array of errors if any |
| 674 | * |
| 675 | * @param {array} $errors list of errors found during a check |
| 676 | * @param {array} $steps list of steps that have to be checked |
| 677 | * @param {array} $role array of step rules to check against |
| 678 | * @returns {array} Filtered list of found errors |
| 679 | * |
| 680 | * @since 5.0.0 |
| 681 | * @hook latepoint_check_steps_for_errors |
| 682 | * |
| 683 | */ |
| 684 | return apply_filters( 'latepoint_check_steps_for_errors', $errors, $steps, $steps_rules ); |
| 685 | |
| 686 | } |
| 687 | |
| 688 | public static function loop_step_rules_check( array $steps, array $steps_rules, string $parent = '' ): array { |
| 689 | $errors = []; |
| 690 | if ( empty( $steps ) ) { |
| 691 | return $errors; |
| 692 | } |
| 693 | |
| 694 | $step_codes_to_validate = array_keys( $steps ); |
| 695 | |
| 696 | $errors = array_merge( $errors, self::validate_step_order( $step_codes_to_validate, $steps_rules, $parent ) ); |
| 697 | |
| 698 | foreach ( $steps as $parent_step_code => $step_children ) { |
| 699 | if ( ! empty( $step_children ) ) { |
| 700 | $errors = array_merge( $errors, self::loop_step_rules_check( $step_children, $steps_rules, $parent_step_code ) ); |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | return $errors; |
| 705 | } |
| 706 | |
| 707 | public static function validate_step_order( array $steps, array $rules, string $parent_code = '' ): array { |
| 708 | $errors = []; |
| 709 | |
| 710 | foreach ( $steps as $step_code ) { |
| 711 | $rule_step_code = $parent_code ? $parent_code . '__' . $step_code : $step_code; |
| 712 | |
| 713 | $current_index = array_search( $step_code, $steps ); |
| 714 | |
| 715 | if ( $current_index === false ) { |
| 716 | continue; // Skip if step is not in steps array |
| 717 | } |
| 718 | |
| 719 | if ( isset( $rules[ $rule_step_code ]['after'] ) ) { |
| 720 | $after_index = array_search( $rules[ $rule_step_code ]['after'], $steps ); |
| 721 | if ( $after_index === false || $after_index >= $current_index ) { |
| 722 | // translators: %1$s is step name with error, %2$s is step that it should come after |
| 723 | $errors[] = sprintf( __( 'Step "%1$s" has to come after "%2$s"', 'latepoint' ), self::get_step_label_by_code( $rule_step_code ), self::get_step_label_by_code( $rules[ $rule_step_code ]['after'], $parent_code ) ); |
| 724 | } |
| 725 | } |
| 726 | |
| 727 | if ( isset( $rules[ $rule_step_code ]['before'] ) ) { |
| 728 | $before_index = array_search( $rules[ $rule_step_code ]['before'], $steps ); |
| 729 | if ( $before_index === false || $before_index <= $current_index ) { |
| 730 | // translators: %1$s is step name with error, %2$s is step that it should come before |
| 731 | $errors[] = sprintf( __( 'Step "%1$s" has to come before "%2$s"', 'latepoint' ), self::get_step_label_by_code( $rule_step_code ), self::get_step_label_by_code( $rules[ $rule_step_code ]['before'], $parent_code ) ); |
| 732 | } |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | return $errors; |
| 737 | } |
| 738 | |
| 739 | /** |
| 740 | * |
| 741 | * Returns a flat and ordered list of step codes |
| 742 | * |
| 743 | * @param bool $show_all_without_saving |
| 744 | * |
| 745 | * @return array |
| 746 | */ |
| 747 | public static function get_step_codes_in_order( bool $show_all_without_saving = false ): array { |
| 748 | if ( $show_all_without_saving ) { |
| 749 | $steps_in_default_order = self::reorder_steps( self::get_step_codes_with_rules() ); |
| 750 | $steps_in_saved_order = self::get_step_codes_in_order_from_db(); |
| 751 | |
| 752 | if ( empty( $steps_in_saved_order ) ) { |
| 753 | $step_codes_in_order = $steps_in_default_order; |
| 754 | } else { |
| 755 | $step_codes_in_order = self::cleanup_steps( $steps_in_saved_order, $steps_in_default_order ); |
| 756 | } |
| 757 | } else { |
| 758 | if ( ! empty( self::$step_codes_in_order ) ) { |
| 759 | return self::$step_codes_in_order; |
| 760 | } |
| 761 | $steps_in_default_order = self::reorder_steps( self::get_step_codes_with_rules() ); |
| 762 | $steps_in_saved_order = self::get_step_codes_in_order_from_db(); |
| 763 | |
| 764 | if ( empty( $steps_in_saved_order ) ) { |
| 765 | // save default active steps and order |
| 766 | $step_codes_in_order = $steps_in_default_order; |
| 767 | self::save_step_codes_in_order( $step_codes_in_order ); |
| 768 | } else { |
| 769 | $step_codes_in_order = self::cleanup_steps( $steps_in_saved_order, $steps_in_default_order ); |
| 770 | // save new order if different from what was saved before |
| 771 | if ( $step_codes_in_order != $steps_in_saved_order ) { |
| 772 | self::save_step_codes_in_order( $step_codes_in_order ); |
| 773 | } |
| 774 | } |
| 775 | self::$step_codes_in_order = $step_codes_in_order; |
| 776 | } |
| 777 | |
| 778 | return $step_codes_in_order; |
| 779 | } |
| 780 | |
| 781 | public static function get_step_codes_in_order_from_db(): array { |
| 782 | $saved_order = OsSettingsHelper::get_settings_value( 'step_codes_in_order', '' ); |
| 783 | if ( ! empty( $saved_order ) ) { |
| 784 | return explode( ',', $saved_order ); |
| 785 | } |
| 786 | |
| 787 | return []; |
| 788 | } |
| 789 | |
| 790 | public static function insert_step( array $ordered_steps, string $new_step, array $new_step_rules ): array { |
| 791 | // Unflatten the ordered steps |
| 792 | $unflattened_steps = self::unflatten_steps( $ordered_steps ); |
| 793 | |
| 794 | // Insert the new step according to its rules |
| 795 | self::insert_step_recursive( $unflattened_steps, $new_step, $new_step_rules ); |
| 796 | |
| 797 | // Flatten the array again |
| 798 | $flattened_steps = self::flatten_steps( $unflattened_steps ); |
| 799 | |
| 800 | return $flattened_steps; |
| 801 | } |
| 802 | |
| 803 | private static function insert_step_recursive( array &$steps, string $new_step, array $new_step_rules ) { |
| 804 | // Split the new step based on its parent structure |
| 805 | $parts = explode( '__', $new_step ); |
| 806 | $actual_step = array_pop( $parts ); |
| 807 | $parent = implode( '__', $parts ) ?: null; |
| 808 | $after = $new_step_rules['after'] ?? null; |
| 809 | |
| 810 | // Insert the new step at the correct position in the unflattened steps |
| 811 | if ( $parent === null ) { |
| 812 | if ( $after === null ) { |
| 813 | // Insert at the beginning if no after rule |
| 814 | $steps = array_merge( [ $actual_step => [] ], $steps ); |
| 815 | } else { |
| 816 | $position = array_search( $after, array_keys( $steps ) ); |
| 817 | if ( $position !== false ) { |
| 818 | $steps = array_slice( $steps, 0, $position + 1, true ) + [ $actual_step => [] ] + array_slice( $steps, $position + 1, null, true ); |
| 819 | } |
| 820 | } |
| 821 | } else { |
| 822 | // Recursively find the correct parent and insert |
| 823 | foreach ( $steps as $step_code => &$step_children ) { |
| 824 | if ( $step_code === $parent ) { |
| 825 | if ( $after === null ) { |
| 826 | $step_children = array_merge( [ $actual_step => [] ], $step_children ); |
| 827 | } else { |
| 828 | $position = array_search( $after, array_keys( $step_children ) ); |
| 829 | if ( $position !== false ) { |
| 830 | $step_children = array_slice( $step_children, 0, $position + 1, true ) + [ $actual_step => [] ] + array_slice( $step_children, $position + 1, null, true ); |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | return; |
| 835 | } else { |
| 836 | self::insert_step_recursive( $step_children, $new_step, $new_step_rules ); |
| 837 | } |
| 838 | } |
| 839 | } |
| 840 | } |
| 841 | |
| 842 | public static function cleanup_steps( array $array_to_clean, array $reference_array ): array { |
| 843 | $filtered_array = []; |
| 844 | foreach ( $array_to_clean as $step_code ) { |
| 845 | if ( in_array( $step_code, $reference_array, true ) ) { |
| 846 | $filtered_array[] = $step_code; |
| 847 | } |
| 848 | } |
| 849 | |
| 850 | $step_codes_with_rules = self::get_step_codes_with_rules(); |
| 851 | foreach ( $reference_array as $step_code ) { |
| 852 | if ( ! in_array( $step_code, $filtered_array ) ) { |
| 853 | $step_rules = $step_codes_with_rules[ $step_code ] ?? []; |
| 854 | $filtered_array = self::insert_step( $filtered_array, $step_code, $step_rules ); |
| 855 | } |
| 856 | } |
| 857 | |
| 858 | return $filtered_array; |
| 859 | } |
| 860 | |
| 861 | public static function get_step_name_without_parent( string $flat_step_name ): string { |
| 862 | $parts = explode( '__', $flat_step_name ); |
| 863 | |
| 864 | return end( $parts ); |
| 865 | } |
| 866 | |
| 867 | |
| 868 | public static function set_default_presets(): array { |
| 869 | self::$presets = self::get_default_presets(); |
| 870 | |
| 871 | return self::$presets; |
| 872 | } |
| 873 | |
| 874 | public static function get_default_presets(): array { |
| 875 | $default_presets = [ |
| 876 | 'selected_bundle' => false, |
| 877 | 'selected_location' => false, |
| 878 | 'selected_agent' => false, |
| 879 | 'selected_service' => false, |
| 880 | 'selected_duration' => false, |
| 881 | 'selected_total_attendees' => false, |
| 882 | 'selected_service_category' => false, |
| 883 | 'selected_start_date' => false, |
| 884 | 'selected_start_time' => false, |
| 885 | 'order_item_id' => false, |
| 886 | 'source_id' => false |
| 887 | ]; |
| 888 | |
| 889 | /** |
| 890 | * Sets default presets array of a StepHelper class |
| 891 | * |
| 892 | * @param {array} $presets Default array of presets set on StepHelper class |
| 893 | * |
| 894 | * @returns {array} Filtered array of presets |
| 895 | * @since 5.0.0 |
| 896 | * @hook latepoint_get_default_presets |
| 897 | * |
| 898 | */ |
| 899 | return apply_filters( 'latepoint_get_default_presets', $default_presets ); |
| 900 | } |
| 901 | |
| 902 | public static function set_default_restrictions(): array { |
| 903 | self::$restrictions = self::get_default_restrictions(); |
| 904 | |
| 905 | return self::$restrictions; |
| 906 | } |
| 907 | |
| 908 | public static function get_default_restrictions(): array { |
| 909 | $default_restrictions = [ |
| 910 | 'show_locations' => false, |
| 911 | 'show_agents' => false, |
| 912 | 'show_services' => false, |
| 913 | 'show_service_categories' => false, |
| 914 | 'calendar_start_date' => false, |
| 915 | ]; |
| 916 | |
| 917 | /** |
| 918 | * Sets default restrictions array of a StepHelper class |
| 919 | * |
| 920 | * @param {array} $restrictions Default array of restrictions set on StepHelper class |
| 921 | * |
| 922 | * @returns {array} Filtered array of restrictions |
| 923 | * @since 5.0.0 |
| 924 | * @hook latepoint_get_default_restrictions |
| 925 | * |
| 926 | */ |
| 927 | return apply_filters( 'latepoint_get_default_restrictions', $default_restrictions ); |
| 928 | } |
| 929 | |
| 930 | public static function set_presets( array $presets = [] ): array { |
| 931 | self::set_default_presets(); |
| 932 | // scheduling an item from existing order (bundle) |
| 933 | if ( isset( $presets['order_item_id'] ) ) { |
| 934 | self::$presets['order_item_id'] = $presets['order_item_id']; |
| 935 | } |
| 936 | |
| 937 | // preselected service category |
| 938 | if ( isset( $presets['selected_service_category'] ) && is_numeric( $presets['selected_service_category'] ) ) { |
| 939 | self::$presets['selected_service_category'] = $presets['selected_service_category']; |
| 940 | } |
| 941 | |
| 942 | // preselected location |
| 943 | if ( ! empty( $presets['selected_location'] ) && ( is_numeric( $presets['selected_location'] ) || ( $presets['selected_location'] == LATEPOINT_ANY_LOCATION ) ) ) { |
| 944 | self::$presets['selected_location'] = $presets['selected_location']; |
| 945 | } |
| 946 | // preselected agent |
| 947 | if ( ! empty( $presets['selected_agent'] ) && ( is_numeric( $presets['selected_agent'] ) || ( $presets['selected_agent'] == LATEPOINT_ANY_AGENT ) ) ) { |
| 948 | self::$presets['selected_agent'] = $presets['selected_agent']; |
| 949 | } |
| 950 | |
| 951 | // preselected service |
| 952 | if ( isset( $presets['selected_service'] ) && is_numeric( $presets['selected_service'] ) ) { |
| 953 | self::$presets['selected_service'] = $presets['selected_service']; |
| 954 | } |
| 955 | |
| 956 | // preselected bundle |
| 957 | if ( isset( $presets['selected_bundle'] ) && is_numeric( $presets['selected_bundle'] ) ) { |
| 958 | self::$presets['selected_bundle'] = $presets['selected_bundle']; |
| 959 | } |
| 960 | |
| 961 | // preselected duration |
| 962 | if ( isset( $presets['selected_duration'] ) && is_numeric( $presets['selected_duration'] ) ) { |
| 963 | self::$presets['selected_duration'] = $presets['selected_duration']; |
| 964 | } |
| 965 | |
| 966 | // preselected total attendees |
| 967 | if ( isset( $presets['selected_total_attendees'] ) && is_numeric( $presets['selected_total_attendees'] ) ) { |
| 968 | self::$presets['selected_total_attendees'] = $presets['selected_total_attendees']; |
| 969 | } |
| 970 | |
| 971 | // preselected date |
| 972 | if ( isset( $presets['selected_start_date'] ) && OsTimeHelper::is_valid_date( $presets['selected_start_date'] ) ) { |
| 973 | self::$presets['selected_start_date'] = $presets['selected_start_date']; |
| 974 | } |
| 975 | |
| 976 | // preselected time |
| 977 | if ( isset( $presets['selected_start_time'] ) && is_numeric( $presets['selected_start_time'] ) ) { |
| 978 | self::$presets['selected_start_time'] = $presets['selected_start_time']; |
| 979 | } |
| 980 | |
| 981 | // set source id |
| 982 | if ( isset( $presets['source_id'] ) ) { |
| 983 | self::$presets['source_id'] = $presets['source_id']; |
| 984 | } |
| 985 | |
| 986 | /** |
| 987 | * Sets presets array of a StepHelper class |
| 988 | * |
| 989 | * @param {array} $presets Array of presets set on StepHelper class |
| 990 | * @param {array} $presets Array of presets to be used to set presets on StepHelper class |
| 991 | * |
| 992 | * @returns {array} Filtered array of presets |
| 993 | * @since 5.0.0 |
| 994 | * @hook latepoint_set_presets |
| 995 | * |
| 996 | */ |
| 997 | return apply_filters( 'latepoint_set_presets', self::$presets, $presets ); |
| 998 | } |
| 999 | |
| 1000 | |
| 1001 | public static function set_restrictions( array $restrictions = [] ): array { |
| 1002 | self::set_default_restrictions(); |
| 1003 | if ( ! empty( $restrictions ) ) { |
| 1004 | // filter locations |
| 1005 | if ( isset( $restrictions['show_locations'] ) ) { |
| 1006 | self::$restrictions['show_locations'] = $restrictions['show_locations']; |
| 1007 | } |
| 1008 | |
| 1009 | // filter agents |
| 1010 | if ( isset( $restrictions['show_agents'] ) ) { |
| 1011 | self::$restrictions['show_agents'] = $restrictions['show_agents']; |
| 1012 | } |
| 1013 | |
| 1014 | // filter service category |
| 1015 | if ( isset( $restrictions['show_service_categories'] ) ) { |
| 1016 | self::$restrictions['show_service_categories'] = $restrictions['show_service_categories']; |
| 1017 | } |
| 1018 | |
| 1019 | // filter services |
| 1020 | if ( isset( $restrictions['show_services'] ) ) { |
| 1021 | self::$restrictions['show_services'] = $restrictions['show_services']; |
| 1022 | } |
| 1023 | |
| 1024 | // preselected calendar start date |
| 1025 | if ( isset( $restrictions['calendar_start_date'] ) && OsTimeHelper::is_valid_date( $restrictions['calendar_start_date'] ) ) { |
| 1026 | self::$restrictions['calendar_start_date'] = $restrictions['calendar_start_date']; |
| 1027 | } |
| 1028 | |
| 1029 | // restriction in settings can override it |
| 1030 | if ( OsTimeHelper::is_valid_date( OsSettingsHelper::get_settings_value( 'earliest_possible_booking' ) ) ) { |
| 1031 | self::$restrictions['calendar_start_date'] = OsSettingsHelper::get_settings_value( 'earliest_possible_booking' ); |
| 1032 | } |
| 1033 | |
| 1034 | |
| 1035 | } |
| 1036 | |
| 1037 | /** |
| 1038 | * Sets restrictions array of a StepHelper class |
| 1039 | * |
| 1040 | * @param {array} $restrictions Array of restrictions set on StepHelper class |
| 1041 | * @param {array} $restrictions Array of restrictions to be used to set restrictions on StepHelper class |
| 1042 | * |
| 1043 | * @returns {array} Filtered array of restrictions |
| 1044 | * @since 5.0.0 |
| 1045 | * @hook latepoint_set_restrictions |
| 1046 | * |
| 1047 | */ |
| 1048 | return apply_filters( 'latepoint_set_restrictions', self::$restrictions, $restrictions ); |
| 1049 | } |
| 1050 | |
| 1051 | /** |
| 1052 | * Sets booking object properties when a single option is available |
| 1053 | * |
| 1054 | * If a booking object has a service selected and only one agent is offering that service - |
| 1055 | * that agent will be preselected. Same for location |
| 1056 | * |
| 1057 | * @return OsBookingModel |
| 1058 | */ |
| 1059 | public static function set_booking_properties_for_single_options(): OsBookingModel { |
| 1060 | |
| 1061 | // if only 1 location exists or assigned to selected agent - set it to this booking object |
| 1062 | if ( OsLocationHelper::count_locations() == 1 ) { |
| 1063 | self::$booking_object->location_id = OsLocationHelper::get_default_location_id(); |
| 1064 | } |
| 1065 | // if only 1 agent exists - set it to this booking object |
| 1066 | if ( OsAgentHelper::count_agents() == 1 ) { |
| 1067 | self::$booking_object->agent_id = OsAgentHelper::get_default_agent_id(); |
| 1068 | } |
| 1069 | |
| 1070 | return self::$booking_object; |
| 1071 | } |
| 1072 | |
| 1073 | public static function set_booking_object( $booking_object_params = [] ): OsBookingModel { |
| 1074 | self::$booking_object = new OsBookingModel(); |
| 1075 | self::$booking_object->set_data( $booking_object_params ); |
| 1076 | if ( ! empty( $booking_object_params['intent_key'] ) ) { |
| 1077 | self::$booking_object->intent_key = $booking_object_params['intent_key']; |
| 1078 | } |
| 1079 | |
| 1080 | // set based on presets |
| 1081 | |
| 1082 | // preselected service |
| 1083 | if ( isset( self::$presets['selected_service'] ) && is_numeric( self::$presets['selected_service'] ) ) { |
| 1084 | self::$booking_object->service_id = self::$presets['selected_service']; |
| 1085 | $service = new OsServiceModel( self::$booking_object->service_id ); |
| 1086 | self::$booking_object->service = $service; |
| 1087 | if ( empty( $booking_object_params['duration'] ) ) { |
| 1088 | self::$booking_object->duration = $service->duration; |
| 1089 | } |
| 1090 | if ( empty( $booking_object_params['total_attendees'] ) ) { |
| 1091 | self::$booking_object->total_attendees = $service->capacity_min; |
| 1092 | } |
| 1093 | } |
| 1094 | |
| 1095 | // preselected agent |
| 1096 | if ( ! empty( self::$presets['selected_agent'] ) && ( is_numeric( self::$presets['selected_agent'] ) || ( self::$presets['selected_agent'] == LATEPOINT_ANY_AGENT ) ) ) { |
| 1097 | self::$booking_object->agent_id = self::$presets['selected_agent']; |
| 1098 | } |
| 1099 | |
| 1100 | // preselected location |
| 1101 | if ( ! empty( self::$presets['selected_location'] ) && ( is_numeric( self::$presets['selected_location'] ) || ( self::$presets['selected_location'] == LATEPOINT_ANY_LOCATION ) ) ) { |
| 1102 | self::$booking_object->location_id = self::$presets['selected_location']; |
| 1103 | } |
| 1104 | |
| 1105 | // preselected duration |
| 1106 | if ( isset( self::$presets['selected_duration'] ) && is_numeric( self::$presets['selected_duration'] ) ) { |
| 1107 | self::$booking_object->duration = self::$presets['selected_duration']; |
| 1108 | } |
| 1109 | // preselected attendees |
| 1110 | if ( isset( self::$presets['selected_total_attendees'] ) && is_numeric( self::$presets['selected_total_attendees'] ) ) { |
| 1111 | self::$booking_object->total_attendees = self::$presets['selected_total_attendees']; |
| 1112 | } |
| 1113 | // preselected date |
| 1114 | if ( isset( self::$presets['selected_start_date'] ) && OsTimeHelper::is_valid_date( self::$presets['selected_start_date'] ) ) { |
| 1115 | self::$booking_object->start_date = self::$presets['selected_start_date']; |
| 1116 | } |
| 1117 | // preselected time |
| 1118 | if ( isset( self::$presets['selected_start_time'] ) && is_numeric( self::$presets['selected_start_time'] ) ) { |
| 1119 | self::$booking_object->start_time = self::$presets['selected_start_time']; |
| 1120 | } |
| 1121 | // preselected time |
| 1122 | if ( isset( self::$presets['order_item_id'] ) && is_numeric( self::$presets['order_item_id'] ) ) { |
| 1123 | self::$booking_object->order_item_id = self::$presets['order_item_id']; |
| 1124 | // TODO - move to pro |
| 1125 | // it's a bundle, preset values from a bundle |
| 1126 | $order_item = new OsOrderItemModel( self::$booking_object->order_item_id ); |
| 1127 | $bundle = new OsBundleModel( $order_item->get_item_data_value_by_key( 'bundle_id' ) ); |
| 1128 | self::$booking_object->total_attendees = $bundle->total_attendees_for_service( self::$booking_object->service_id ); |
| 1129 | self::$booking_object->duration = $bundle->duration_for_service( self::$booking_object->service_id ); |
| 1130 | } |
| 1131 | |
| 1132 | |
| 1133 | // get buffers from service and set to booking object |
| 1134 | self::$booking_object->set_buffers(); |
| 1135 | self::$booking_object->calculate_end_date_and_time(); |
| 1136 | self::$booking_object->customer_id = OsAuthHelper::get_logged_in_customer_id(); |
| 1137 | |
| 1138 | return self::$booking_object; |
| 1139 | } |
| 1140 | |
| 1141 | public static function load_order_object( $order_id = false ) { |
| 1142 | if ( $order_id ) { |
| 1143 | self::$order_object = new OsOrderModel( $order_id ); |
| 1144 | } else { |
| 1145 | self::$order_object = new OsOrderModel(); |
| 1146 | } |
| 1147 | } |
| 1148 | |
| 1149 | public static function is_bundle_scheduling() { |
| 1150 | return ! empty( self::$booking_object->order_item_id ); |
| 1151 | } |
| 1152 | |
| 1153 | /** |
| 1154 | * Checks if there were supposed to be some fields for this step - now they have to be carried over to next step, because this step is skipped |
| 1155 | * |
| 1156 | * @param string $current_step_code |
| 1157 | * @param string $next_step_code |
| 1158 | * |
| 1159 | * @return array |
| 1160 | */ |
| 1161 | public static function carry_preset_fields_to_next_step( string $current_step_code, string $next_step_code ): void { |
| 1162 | if ( ! empty( self::$preset_fields[ $current_step_code ] ) ) { |
| 1163 | self::$preset_fields[ $next_step_code ] = array_merge( self::$preset_fields[ $next_step_code ], self::$preset_fields[ $current_step_code ] ); |
| 1164 | } |
| 1165 | } |
| 1166 | |
| 1167 | public static function should_step_be_skipped( string $step_code ): bool { |
| 1168 | $skip = false; |
| 1169 | |
| 1170 | switch ( $step_code ) { |
| 1171 | case 'booking__agents': |
| 1172 | if ( OsAgentHelper::count_agents() == 1 ) { |
| 1173 | $skip = true; |
| 1174 | } |
| 1175 | if ( self::$active_cart_item->is_bundle() ) { |
| 1176 | $skip = true; |
| 1177 | } |
| 1178 | break; |
| 1179 | case 'booking__locations': |
| 1180 | if ( OsLocationHelper::count_locations() == 1 ) { |
| 1181 | $skip = true; |
| 1182 | } |
| 1183 | if ( self::$active_cart_item->is_bundle() ) { |
| 1184 | $skip = true; |
| 1185 | } |
| 1186 | break; |
| 1187 | case 'booking__datepicker': |
| 1188 | if ( self::$active_cart_item->is_bundle() ) { |
| 1189 | $skip = true; |
| 1190 | } |
| 1191 | break; |
| 1192 | case 'booking__services': |
| 1193 | if ( self::is_bundle_scheduling() ) { |
| 1194 | $skip = true; |
| 1195 | } |
| 1196 | break; |
| 1197 | case 'payment__times': |
| 1198 | case 'payment__portions': |
| 1199 | case 'payment__methods': |
| 1200 | case 'payment__processors': |
| 1201 | case 'payment__pay': |
| 1202 | if ( self::is_bundle_scheduling() || empty( OsPaymentsHelper::get_enabled_payment_times() ) ) { |
| 1203 | // scheduling a bundle or no enabled payment times |
| 1204 | $skip = true; |
| 1205 | self::set_zero_cost_payment_fields(); |
| 1206 | } else { |
| 1207 | if(self::$cart_object->is_empty()){ |
| 1208 | $skip = true; |
| 1209 | }else{ |
| 1210 | $original_amount = self::$cart_object->get_subtotal(); |
| 1211 | $after_coupons_amount = self::$cart_object->get_total(); |
| 1212 | $deposit_amount = self::$cart_object->deposit_amount_to_charge(); |
| 1213 | if ( $original_amount > 0 && $after_coupons_amount <= 0 ) { |
| 1214 | // original price was set, but coupon was applied and charge amount is now 0, we can skip step, even if deposit is not 0 |
| 1215 | $is_zero_cost = true; |
| 1216 | } else { |
| 1217 | if ( $after_coupons_amount <= 0 && $deposit_amount <= 0 ) { |
| 1218 | $is_zero_cost = true; |
| 1219 | } else { |
| 1220 | $is_zero_cost = false; |
| 1221 | } |
| 1222 | } |
| 1223 | // if nothing to charge - don't show it, no matter what |
| 1224 | if ( $is_zero_cost && ! OsSettingsHelper::is_env_demo() ) { |
| 1225 | $skip = true; |
| 1226 | self::set_zero_cost_payment_fields(); |
| 1227 | } else { |
| 1228 | if ( $step_code == 'payment__times' ) { |
| 1229 | if ( ! empty( self::$cart_object->payment_time ) ) { |
| 1230 | $skip = true; |
| 1231 | } else { |
| 1232 | // try to check if one only available and preset it |
| 1233 | $enabled_payment_times = OsPaymentsHelper::get_enabled_payment_times(); |
| 1234 | if ( count( $enabled_payment_times ) == 1 ) { |
| 1235 | $skip = true; |
| 1236 | self::$cart_object->payment_time = array_key_first( $enabled_payment_times ); |
| 1237 | self::$preset_fields['verify']['cart[payment_time]'] = OsFormHelper::hidden_field( 'cart[payment_time]', self::$cart_object->payment_time, [ 'skip_id' => true ] ); |
| 1238 | // assign preset field value for next step |
| 1239 | self::$preset_fields['payment__portions']['cart[payment_time]'] = OsFormHelper::hidden_field( 'cart[payment_time]', self::$cart_object->payment_time, [ 'skip_id' => true ] ); |
| 1240 | self::carry_preset_fields_to_next_step( 'payment__times', 'payment__portions' ); |
| 1241 | } |
| 1242 | } |
| 1243 | } |
| 1244 | if ( $step_code == 'payment__portions' ) { |
| 1245 | if ( ! empty( self::$cart_object->payment_portion ) ) { |
| 1246 | $skip = true; |
| 1247 | } else { |
| 1248 | if ( $is_zero_cost || ( self::$cart_object->payment_time == LATEPOINT_PAYMENT_TIME_LATER ) || ( $after_coupons_amount > 0 && $deposit_amount <= 0 ) ) { |
| 1249 | // zero cost, pay later or 0 deposit, means it's a full portion payment preset |
| 1250 | self::$cart_object->payment_portion = LATEPOINT_PAYMENT_PORTION_FULL; |
| 1251 | } elseif ( $deposit_amount > 0 && $after_coupons_amount <= 0 ) { |
| 1252 | self::$cart_object->payment_portion = LATEPOINT_PAYMENT_PORTION_DEPOSIT; |
| 1253 | } |
| 1254 | |
| 1255 | if ( ! empty( self::$cart_object->payment_portion ) ) { |
| 1256 | $skip = true; |
| 1257 | self::$preset_fields['verify']['cart[payment_portion]'] = OsFormHelper::hidden_field( 'cart[payment_portion]', self::$cart_object->payment_portion, [ 'skip_id' => true ] ); |
| 1258 | self::$preset_fields['payment__methods']['cart[payment_portion]'] = OsFormHelper::hidden_field( 'cart[payment_portion]', self::$cart_object->payment_portion, [ 'skip_id' => true ] ); |
| 1259 | |
| 1260 | self::carry_preset_fields_to_next_step( 'payment__portions', 'payment__methods' ); |
| 1261 | } |
| 1262 | } |
| 1263 | } |
| 1264 | if ( $step_code == 'payment__methods' ) { |
| 1265 | if ( ! empty( self::$cart_object->payment_method ) ) { |
| 1266 | $skip = true; |
| 1267 | } else { |
| 1268 | if ( self::$cart_object->payment_time ) { |
| 1269 | $enabled_payment_methods = OsPaymentsHelper::get_enabled_payment_methods_for_payment_time( self::$cart_object->payment_time ); |
| 1270 | if ( count( $enabled_payment_methods ) <= 1 ) { |
| 1271 | $skip = true; |
| 1272 | self::$cart_object->payment_method = array_key_first( $enabled_payment_methods ); |
| 1273 | self::$preset_fields['verify']['cart[payment_method]'] = OsFormHelper::hidden_field( 'cart[payment_method]', self::$cart_object->payment_method, [ 'skip_id' => true ] ); |
| 1274 | self::$preset_fields['payment__processors']['cart[payment_method]'] = OsFormHelper::hidden_field( 'cart[payment_method]', self::$cart_object->payment_method, [ 'skip_id' => true ] ); |
| 1275 | |
| 1276 | self::carry_preset_fields_to_next_step( 'payment__methods', 'payment__processors' ); |
| 1277 | } |
| 1278 | } |
| 1279 | } |
| 1280 | } |
| 1281 | if ( $step_code == 'payment__processors' ) { |
| 1282 | if ( ! empty( self::$cart_object->payment_processor ) ) { |
| 1283 | $skip = true; |
| 1284 | } else { |
| 1285 | if ( self::$cart_object->payment_time && self::$cart_object->payment_method ) { |
| 1286 | $enabled_payment_processors = OsPaymentsHelper::get_enabled_payment_processors_for_payment_time_and_method( self::$cart_object->payment_time, self::$cart_object->payment_method ); |
| 1287 | if ( count( $enabled_payment_processors ) <= 1 ) { |
| 1288 | $skip = true; |
| 1289 | self::$cart_object->payment_processor = array_key_first( $enabled_payment_processors ); |
| 1290 | self::$preset_fields['verify']['cart[payment_processor]'] = OsFormHelper::hidden_field( 'cart[payment_processor]', self::$cart_object->payment_processor, [ 'skip_id' => true ] ); |
| 1291 | self::$preset_fields['payment__pay']['cart[payment_processor]'] = OsFormHelper::hidden_field( 'cart[payment_processor]', self::$cart_object->payment_processor, [ 'skip_id' => true ] ); |
| 1292 | |
| 1293 | self::carry_preset_fields_to_next_step( 'payment__processors', 'payment__pay' ); |
| 1294 | } |
| 1295 | } |
| 1296 | } |
| 1297 | } |
| 1298 | if ( $step_code == 'payment__pay' ) { |
| 1299 | if ( self::$cart_object->payment_time == LATEPOINT_PAYMENT_TIME_LATER || empty( OsPaymentsHelper::get_enabled_payment_times() ) ) { |
| 1300 | $skip = true; |
| 1301 | } |
| 1302 | } |
| 1303 | } |
| 1304 | } |
| 1305 | } |
| 1306 | break; |
| 1307 | } |
| 1308 | |
| 1309 | $skip = apply_filters( 'latepoint_should_step_be_skipped', $skip, $step_code, self::$cart_object, self::$active_cart_item, self::$booking_object ); |
| 1310 | |
| 1311 | return $skip; |
| 1312 | } |
| 1313 | |
| 1314 | public static function set_zero_cost_payment_fields() { |
| 1315 | self::$preset_fields['verify']['cart[payment_time]'] = OsFormHelper::hidden_field( 'cart[payment_time]', LATEPOINT_PAYMENT_TIME_LATER, [ 'skip_id' => true ] ); |
| 1316 | self::$preset_fields['verify']['cart[payment_method]'] = OsFormHelper::hidden_field( 'cart[payment_method]', 'other', [ 'skip_id' => true ] ); |
| 1317 | self::$preset_fields['verify']['cart[payment_processor]'] = OsFormHelper::hidden_field( 'cart[payment_processor]', 'other', [ 'skip_id' => true ] ); |
| 1318 | self::$preset_fields['verify']['cart[payment_portion]'] = OsFormHelper::hidden_field( 'cart[payment_portion]', LATEPOINT_PAYMENT_PORTION_FULL, [ 'skip_id' => true ] ); |
| 1319 | } |
| 1320 | |
| 1321 | public static function output_preset_fields( string $step_code ) { |
| 1322 | if ( ! empty( self::$preset_fields[ $step_code ] ) ) { |
| 1323 | foreach ( self::$preset_fields[ $step_code ] as $preset_field_html ) { |
| 1324 | echo $preset_field_html; |
| 1325 | } |
| 1326 | } |
| 1327 | } |
| 1328 | |
| 1329 | public static function get_next_step_code( $current_step_code ) { |
| 1330 | $all_step_codes = self::get_step_codes_in_order( true ); |
| 1331 | $active_step_codes = self::get_step_codes_in_order(); |
| 1332 | $current_step_index = array_search( $current_step_code, $all_step_codes ); |
| 1333 | if ( $current_step_index === false || ( ( $current_step_index + 1 ) == count( $all_step_codes ) ) ) { |
| 1334 | // no more steps or not found |
| 1335 | return false; |
| 1336 | } |
| 1337 | $next_step_code = $all_step_codes[ $current_step_index + 1 ]; |
| 1338 | |
| 1339 | if ( ! in_array( $next_step_code, $active_step_codes ) ) { |
| 1340 | // if is skipped - get next step in order and try again |
| 1341 | $next_step_code = self::get_next_step_code( $next_step_code ); |
| 1342 | } |
| 1343 | |
| 1344 | /** |
| 1345 | * Get the next step code, based on a current step |
| 1346 | * |
| 1347 | * @param {string} $next_step_code The next step code |
| 1348 | * @param {string} $current_step_code The current step code |
| 1349 | * @param {array} $all_step_codes List of all step codes |
| 1350 | * @param {array} $active_step_codes List of active step codes |
| 1351 | * @returns {string} The filtered next step code |
| 1352 | * |
| 1353 | * @since 5.0.16 |
| 1354 | * @hook latepoint_get_next_step_code |
| 1355 | * |
| 1356 | */ |
| 1357 | return apply_filters('latepoint_get_next_step_code', $next_step_code, $current_step_code, $all_step_codes, $active_step_codes); |
| 1358 | } |
| 1359 | |
| 1360 | public static function get_prev_step_code( $current_step_code ) { |
| 1361 | $all_step_codes = self::get_step_codes_in_order( true ); |
| 1362 | $current_step_index = array_search( $current_step_code, $all_step_codes ); |
| 1363 | |
| 1364 | if ( ! $current_step_index ) { |
| 1365 | // first step or not found - return the same code |
| 1366 | return $current_step_code; |
| 1367 | } |
| 1368 | $prev_step_code = $all_step_codes[ $current_step_code - 1 ]; |
| 1369 | if ( self::should_step_be_skipped( $prev_step_code ) ) { |
| 1370 | // if skipped - get previous in order and try again |
| 1371 | $prev_step_code = self::get_prev_step_code( $prev_step_code ); |
| 1372 | } |
| 1373 | |
| 1374 | /** |
| 1375 | * Get the next step code, based on a current step |
| 1376 | * |
| 1377 | * @param {string} $next_step_code The next step code |
| 1378 | * @param {string} $current_step_code The current step code |
| 1379 | * @param {array} $all_step_codes List of all step codes |
| 1380 | * @returns {string} The filtered next step code |
| 1381 | * |
| 1382 | * @since 5.0.16 |
| 1383 | * @hook latepoint_get_previous_step_code |
| 1384 | * |
| 1385 | */ |
| 1386 | return apply_filters('latepoint_get_previous_step_code', $prev_step_code, $current_step_code, $all_step_codes); |
| 1387 | } |
| 1388 | |
| 1389 | |
| 1390 | public static function is_first_step( $step_code ) { |
| 1391 | $step_index = array_search( $step_code, self::get_step_codes_in_order() ); |
| 1392 | |
| 1393 | return $step_index == 0; |
| 1394 | } |
| 1395 | |
| 1396 | public static function is_last_step( $step_code ) { |
| 1397 | $step_index = array_search( $step_code, self::get_step_codes_in_order() ); |
| 1398 | |
| 1399 | return ( ( $step_index + 1 ) == count( self::get_step_codes_in_order() ) ); |
| 1400 | } |
| 1401 | |
| 1402 | public static function is_pre_last_step( $step_code ) { |
| 1403 | $next_step_code = self::get_next_step_code( $step_code ); |
| 1404 | $step_index = array_search( $next_step_code, self::get_step_codes_in_order() ); |
| 1405 | |
| 1406 | return ( ( $step_index + 1 ) == count( self::get_step_codes_in_order() ) ); |
| 1407 | } |
| 1408 | |
| 1409 | public static function can_step_show_prev_btn( $step_code ) { |
| 1410 | $step_index = array_search( $step_code, self::get_step_codes_in_order() ); |
| 1411 | // if first or last step |
| 1412 | if ( $step_index == 0 || ( ( $step_index + 1 ) == count( self::get_step_codes_in_order() ) ) ) { |
| 1413 | return false; |
| 1414 | } else { |
| 1415 | return true; |
| 1416 | } |
| 1417 | } |
| 1418 | |
| 1419 | public static function get_next_btn_label_for_step( $step_code ) { |
| 1420 | $label = __( 'Next', 'latepoint' ); |
| 1421 | $custom_labels = [ |
| 1422 | 'payment__pay' => __( 'Submit', 'latepoint' ), |
| 1423 | 'verify' => OsStepsHelper::should_step_be_skipped( 'payment__pay' ) ? __( 'Submit', 'latepoint' ) : __( 'Checkout', 'latepoint' ) |
| 1424 | ]; |
| 1425 | |
| 1426 | |
| 1427 | /** |
| 1428 | * Returns an array of custom labels for "next" button with step codes as keys |
| 1429 | * |
| 1430 | * @param {array} $custom_labels Current array of labels for "next" button |
| 1431 | * |
| 1432 | * @returns {array} Filtered array of labels for "next" button |
| 1433 | * @since 4.7.0 |
| 1434 | * @hook latepoint_next_btn_labels_for_steps |
| 1435 | * |
| 1436 | */ |
| 1437 | $custom_labels = apply_filters( 'latepoint_next_btn_labels_for_steps', $custom_labels ); |
| 1438 | if ( ! empty( $custom_labels[ $step_code ] ) ) { |
| 1439 | $label = $custom_labels[ $step_code ]; |
| 1440 | } |
| 1441 | |
| 1442 | return $label; |
| 1443 | } |
| 1444 | |
| 1445 | public static function can_step_show_next_btn( $step_code ) { |
| 1446 | $step_show_btn_rules = [ |
| 1447 | 'booking__services' => false, |
| 1448 | 'booking__agents' => false, |
| 1449 | 'booking__datepicker' => false, |
| 1450 | 'customer' => true, |
| 1451 | 'payment__times' => false, |
| 1452 | 'payment__portions' => false, |
| 1453 | 'payment__methods' => false, |
| 1454 | 'payment__pay' => false, |
| 1455 | 'verify' => true, |
| 1456 | 'confirmation' => false |
| 1457 | ]; |
| 1458 | |
| 1459 | /** |
| 1460 | * Returns an array of rules of whether to show a next button on not, step codes are keys in this array |
| 1461 | * |
| 1462 | * @param {array} $step_show_btn_rules Current array of labels for "next" button |
| 1463 | * @param {string} $step_code Current array of labels for "next" button |
| 1464 | * |
| 1465 | * @returns {array} Filtered array of labels for "next" button |
| 1466 | * @since 4.7.0 |
| 1467 | * @hook latepoint_step_show_next_btn_rules |
| 1468 | * |
| 1469 | */ |
| 1470 | $step_show_btn_rules = apply_filters( 'latepoint_step_show_next_btn_rules', $step_show_btn_rules, $step_code ); |
| 1471 | |
| 1472 | return $step_show_btn_rules[ $step_code ] ?? false; |
| 1473 | } |
| 1474 | |
| 1475 | /** |
| 1476 | * @throws Exception |
| 1477 | */ |
| 1478 | public static function add_current_item_to_cart() { |
| 1479 | if ( self::$active_cart_item->is_new_record() ) { |
| 1480 | if ( self::$active_cart_item->is_bundle() ) { |
| 1481 | self::$cart_object->add_item( self::$active_cart_item ); |
| 1482 | self::$fields_to_update['active_cart_item[id]'] = self::$active_cart_item->id; |
| 1483 | } elseif ( self::$active_cart_item->is_booking() ) { |
| 1484 | // only do this for new cart item, if modifying existing one - then the set_active_cart_item method will take care of updating it |
| 1485 | if ( self::$booking_object->is_bookable( true, true ) ) { |
| 1486 | // set it again as booking object might have changed if agent or location were set to ANY, they are assigned now |
| 1487 | self::set_active_cart_item_object(); |
| 1488 | if ( self::is_bundle_scheduling() ) { |
| 1489 | // we don't need to use a cart for bundle scheduling |
| 1490 | } else { |
| 1491 | self::$cart_object->add_item( self::$active_cart_item ); |
| 1492 | self::$fields_to_update['active_cart_item[id]'] = self::$active_cart_item->id; |
| 1493 | } |
| 1494 | self::reset_booking_object(); |
| 1495 | |
| 1496 | return true; |
| 1497 | } else { |
| 1498 | throw new Exception( implode( ',', self::$booking_object->get_error_messages() ) ); |
| 1499 | } |
| 1500 | } |
| 1501 | } |
| 1502 | } |
| 1503 | |
| 1504 | public static function process_step_booking() { |
| 1505 | |
| 1506 | if ( ! self::is_bundle_scheduling() ) { |
| 1507 | // check if we are processing the last step of a booking sequence |
| 1508 | $booking_steps = []; |
| 1509 | foreach ( self::$step_codes_in_order as $step_code ) { |
| 1510 | if ( strpos( $step_code, 'booking__' ) !== false ) { |
| 1511 | $booking_steps[] = $step_code; |
| 1512 | } |
| 1513 | } |
| 1514 | if ( end( $booking_steps ) == self::$step_to_process ) { |
| 1515 | try { |
| 1516 | self::add_current_item_to_cart(); |
| 1517 | } catch ( Exception $e ) { |
| 1518 | return new WP_Error( 'booking_slot_not_available', $e->getMessage() ); |
| 1519 | } |
| 1520 | } |
| 1521 | } |
| 1522 | |
| 1523 | |
| 1524 | } |
| 1525 | |
| 1526 | public static function reset_booking_object() { |
| 1527 | self::set_booking_object( [] ); |
| 1528 | } |
| 1529 | |
| 1530 | public static function prepare_step_booking() { |
| 1531 | |
| 1532 | } |
| 1533 | |
| 1534 | |
| 1535 | // SERVICES |
| 1536 | |
| 1537 | public static function process_step_booking__services() { |
| 1538 | } |
| 1539 | |
| 1540 | public static function prepare_step_booking__services() { |
| 1541 | $bundles_model = new OsBundleModel(); |
| 1542 | $bundles = $bundles_model->should_be_active()->should_not_be_hidden()->get_results_as_models(); |
| 1543 | |
| 1544 | $services_model = new OsServiceModel(); |
| 1545 | $show_selected_services_arr = self::$restrictions['show_services'] ? explode( ',', self::$restrictions['show_services'] ) : false; |
| 1546 | $show_service_categories_arr = self::$restrictions['show_service_categories'] ? explode( ',', self::$restrictions['show_service_categories'] ) : false; |
| 1547 | $preselected_category = self::$presets['selected_service_category']; |
| 1548 | $preselected_duration = self::$presets['selected_duration']; |
| 1549 | $preselected_total_attendees = self::$presets['selected_total_attendees']; |
| 1550 | |
| 1551 | $connected_ids = OsConnectorHelper::get_connected_object_ids( 'service_id', [ |
| 1552 | 'agent_id' => self::$booking_object->agent_id, |
| 1553 | 'location_id' => self::$booking_object->location_id |
| 1554 | ] ); |
| 1555 | // if "show only specific services" is selected (restrictions) - remove ids that are not found in connection |
| 1556 | $show_services_arr = ( ! empty( $show_selected_services_arr ) && ! empty( $connected_ids ) ) ? array_intersect( $connected_ids, $show_selected_services_arr ) : $connected_ids; |
| 1557 | if ( ! empty( $show_services_arr ) ) { |
| 1558 | $services_model->where_in( 'id', $show_services_arr ); |
| 1559 | } |
| 1560 | |
| 1561 | $services = $services_model->should_be_active()->should_not_be_hidden()->order_by( 'order_number asc' )->get_results_as_models(); |
| 1562 | |
| 1563 | self::$vars_for_view['show_services_arr'] = $show_services_arr; |
| 1564 | self::$vars_for_view['show_service_categories_arr'] = $show_service_categories_arr; |
| 1565 | self::$vars_for_view['preselected_category'] = $preselected_category; |
| 1566 | self::$vars_for_view['preselected_duration'] = $preselected_duration; |
| 1567 | self::$vars_for_view['preselected_total_attendees'] = $preselected_total_attendees; |
| 1568 | self::$vars_for_view['services'] = $services; |
| 1569 | self::$vars_for_view['bundles'] = $bundles; |
| 1570 | } |
| 1571 | |
| 1572 | // AGENTS |
| 1573 | |
| 1574 | public static function process_step_booking__agents() { |
| 1575 | } |
| 1576 | |
| 1577 | public static function prepare_step_booking__agents() { |
| 1578 | $agents_model = new OsAgentModel(); |
| 1579 | |
| 1580 | $show_selected_agents_arr = ( self::$restrictions['show_agents'] ) ? explode( ',', self::$restrictions['show_agents'] ) : false; |
| 1581 | // Find agents that actually offer selected service (if selected) at selected location (if selected) |
| 1582 | $connected_ids = OsConnectorHelper::get_connected_object_ids( 'agent_id', [ |
| 1583 | 'service_id' => self::$booking_object->service_id, |
| 1584 | 'location_id' => self::$booking_object->location_id |
| 1585 | ] ); |
| 1586 | |
| 1587 | // If date/time is selected - filter agents who are available at that time |
| 1588 | if ( self::$booking_object->start_date && self::$booking_object->start_time ) { |
| 1589 | $available_agent_ids = []; |
| 1590 | $booking_request = \LatePoint\Misc\BookingRequest::create_from_booking_model( self::$booking_object ); |
| 1591 | foreach ( $connected_ids as $agent_id ) { |
| 1592 | $booking_request->agent_id = $agent_id; |
| 1593 | if ( OsBookingHelper::is_booking_request_available( $booking_request ) ) { |
| 1594 | $available_agent_ids[] = $agent_id; |
| 1595 | } |
| 1596 | } |
| 1597 | $connected_ids = array_intersect( $available_agent_ids, $connected_ids ); |
| 1598 | } |
| 1599 | |
| 1600 | |
| 1601 | // if show only specific agents are selected (restrictions) - remove ids that are not found in connection |
| 1602 | $show_agents_arr = ( $show_selected_agents_arr ) ? array_intersect( $connected_ids, $show_selected_agents_arr ) : $connected_ids; |
| 1603 | if ( ! empty( $show_agents_arr ) ) { |
| 1604 | $agents_model->where_in( 'id', $show_agents_arr ); |
| 1605 | $agents = $agents_model->should_be_active()->get_results_as_models(); |
| 1606 | self::$vars_for_view['agents'] = $agents; |
| 1607 | } else { |
| 1608 | // no available or connected agents |
| 1609 | self::$vars_for_view['agents'] = []; |
| 1610 | } |
| 1611 | } |
| 1612 | |
| 1613 | |
| 1614 | // DATEPICKER |
| 1615 | |
| 1616 | public static function prepare_step_booking__datepicker() { |
| 1617 | if ( empty( self::$booking_object->agent_id ) ) { |
| 1618 | self::$booking_object->agent_id = LATEPOINT_ANY_AGENT; |
| 1619 | } |
| 1620 | self::$vars_for_view['calendar_start_date'] = self::$restrictions['calendar_start_date'] ? self::$restrictions['calendar_start_date'] : 'today'; |
| 1621 | self::$vars_for_view['timeshift_minutes'] = OsTimeHelper::get_timezone_shift_in_minutes_from_session(); |
| 1622 | self::$vars_for_view['timezone_name'] = OsTimeHelper::get_timezone_name_from_session(); |
| 1623 | } |
| 1624 | |
| 1625 | public static function process_step_booking__datepicker() { |
| 1626 | } |
| 1627 | |
| 1628 | |
| 1629 | // CONTACT |
| 1630 | |
| 1631 | |
| 1632 | public static function prepare_step_customer() { |
| 1633 | |
| 1634 | if ( OsAuthHelper::is_customer_logged_in() ) { |
| 1635 | self::$booking_object->customer = OsAuthHelper::get_logged_in_customer(); |
| 1636 | self::$booking_object->customer_id = self::$booking_object->customer->id; |
| 1637 | } else { |
| 1638 | self::$booking_object->customer = new OsCustomerModel(); |
| 1639 | } |
| 1640 | |
| 1641 | self::$vars_for_view['default_fields_for_customer'] = OsSettingsHelper::get_default_fields_for_customer(); |
| 1642 | self::$vars_for_view['customer'] = self::$booking_object->customer; |
| 1643 | } |
| 1644 | |
| 1645 | private static function customer_params(): array { |
| 1646 | $params = OsParamsHelper::get_param( 'customer' ); |
| 1647 | if ( empty( $params ) ) { |
| 1648 | return []; |
| 1649 | } |
| 1650 | |
| 1651 | $customer_params = OsParamsHelper::permit_params( $params, [ |
| 1652 | 'first_name', |
| 1653 | 'last_name', |
| 1654 | 'email', |
| 1655 | 'phone', |
| 1656 | 'notes', |
| 1657 | 'password', |
| 1658 | 'password_confirmation' |
| 1659 | ] ); |
| 1660 | |
| 1661 | if ( ! empty( $customer_params['first_name'] ) ) { |
| 1662 | $customer_params['first_name'] = sanitize_text_field( $customer_params['first_name'] ); |
| 1663 | } |
| 1664 | if ( ! empty( $customer_params['last_name'] ) ) { |
| 1665 | $customer_params['last_name'] = sanitize_text_field( $customer_params['last_name'] ); |
| 1666 | } |
| 1667 | if ( ! empty( $customer_params['email'] ) ) { |
| 1668 | $customer_params['email'] = sanitize_email( $customer_params['email'] ); |
| 1669 | } |
| 1670 | if ( ! empty( $customer_params['phone'] ) ) { |
| 1671 | $customer_params['phone'] = sanitize_text_field( $customer_params['phone'] ); |
| 1672 | } |
| 1673 | if ( ! empty( $customer_params['notes'] ) ) { |
| 1674 | $customer_params['notes'] = sanitize_textarea_field( $customer_params['notes'] ); |
| 1675 | } |
| 1676 | |
| 1677 | /** |
| 1678 | * Filtered customer params for steps |
| 1679 | * |
| 1680 | * @param {array} $customer_params a filtered array of customer params |
| 1681 | * @param {array} $params unfiltered 'customer' params |
| 1682 | * @returns {array} $customer_params a filtered array of customer params |
| 1683 | * |
| 1684 | * @since 5.0.14 |
| 1685 | * @hook latepoint_customer_params_on_steps |
| 1686 | * |
| 1687 | */ |
| 1688 | return apply_filters( 'latepoint_customer_params_on_steps', $customer_params, $params ); |
| 1689 | } |
| 1690 | |
| 1691 | public static function process_step_customer() { |
| 1692 | $status = LATEPOINT_STATUS_SUCCESS; |
| 1693 | |
| 1694 | $customer_params = self::customer_params(); |
| 1695 | |
| 1696 | $logged_in_customer = OsAuthHelper::get_logged_in_customer(); |
| 1697 | |
| 1698 | |
| 1699 | if ( $logged_in_customer ) { |
| 1700 | // LOGGED IN ALREADY |
| 1701 | // Check if they are changing the email on file |
| 1702 | if ( $logged_in_customer->email != $customer_params['email'] ) { |
| 1703 | // Check if other customer already has this email |
| 1704 | $customer = new OsCustomerModel(); |
| 1705 | $customer_with_email_exist = $customer->where( array( |
| 1706 | 'email' => $customer_params['email'], |
| 1707 | 'id !=' => $logged_in_customer->id |
| 1708 | ) )->set_limit( 1 )->get_results_as_models(); |
| 1709 | // check if another customer (or if wp user login enabled - another wp user) exists with the email that this user tries to update to |
| 1710 | if ( $customer_with_email_exist || ( OsAuthHelper::wp_users_as_customers() && email_exists( $customer_params['email'] ) ) ) { |
| 1711 | $status = LATEPOINT_STATUS_ERROR; |
| 1712 | $response_html = __( 'Another customer is registered with this email.', 'latepoint' ); |
| 1713 | } |
| 1714 | } |
| 1715 | } else { |
| 1716 | // NEW REGISTRATION (NOT LOGGED IN) |
| 1717 | if ( OsAuthHelper::wp_users_as_customers() ) { |
| 1718 | // WP USERS AS CUSTOMERS |
| 1719 | if ( email_exists( $customer_params['email'] ) ) { |
| 1720 | // wordpress user with this email already exists, ask to login |
| 1721 | $status = LATEPOINT_STATUS_ERROR; |
| 1722 | $response_html = __( 'An account with that email address already exists. Please try signing in.', 'latepoint' ); |
| 1723 | } else { |
| 1724 | // wp user does not exist - search for latepoint customer |
| 1725 | $customer = new OsCustomerModel(); |
| 1726 | $customer = $customer->where( array( 'email' => $customer_params['email'] ) )->set_limit( 1 )->get_results_as_models(); |
| 1727 | if ( $customer ) { |
| 1728 | // latepoint customer with this email exits, create wp user for them |
| 1729 | $wp_user = OsCustomerHelper::create_wp_user_for_customer( $customer ); |
| 1730 | $status = LATEPOINT_STATUS_ERROR; |
| 1731 | $response_html = __( 'An account with that email address already exists. Please try signing in.', 'latepoint' ); |
| 1732 | } else { |
| 1733 | // no latepoint customer or wp user with this email found, can proceed |
| 1734 | } |
| 1735 | } |
| 1736 | } else { |
| 1737 | // LATEPOINT CUSTOMERS |
| 1738 | $customer = new OsCustomerModel(); |
| 1739 | $customer_exist = $customer->where( array( 'email' => $customer_params['email'] ) )->set_limit( 1 )->get_results_as_models(); |
| 1740 | if ( $customer_exist ) { |
| 1741 | // customer with this email exists - check if current customer was registered as a guest |
| 1742 | if ( OsSettingsHelper::is_on( 'steps_hide_login_register_tabs' ) || ( $customer_exist->can_login_without_password() && ! OsSettingsHelper::is_on( 'steps_require_setting_password' ) ) ) { |
| 1743 | // guest account, login automatically |
| 1744 | $status == LATEPOINT_STATUS_SUCCESS; |
| 1745 | OsAuthHelper::authorize_customer( $customer_exist->id ); |
| 1746 | } else { |
| 1747 | // Not a guest account, ask to login |
| 1748 | $status = LATEPOINT_STATUS_ERROR; |
| 1749 | $response_html = __( 'An account with that email address already exists. Please try signing in.', 'latepoint' ); |
| 1750 | } |
| 1751 | } else { |
| 1752 | // no latepoint customer with this email found, can proceed |
| 1753 | } |
| 1754 | } |
| 1755 | // if not logged in - check if password has to be set |
| 1756 | if ( ! OsAuthHelper::is_customer_logged_in() && OsSettingsHelper::is_on( 'steps_require_setting_password' ) ) { |
| 1757 | if ( ! empty( $customer_params['password'] ) && $customer_params['password'] == $customer_params['password_confirmation'] ) { |
| 1758 | $customer_params['password'] = OsAuthHelper::hash_password( $customer_params['password'] ); |
| 1759 | $customer_params['is_guest'] = false; |
| 1760 | } else { |
| 1761 | // Password is blank or does not match the confirmation |
| 1762 | $status = LATEPOINT_STATUS_ERROR; |
| 1763 | $response_html = __( 'Setting password is required and should match password confirmation', 'latepoint' ); |
| 1764 | } |
| 1765 | } |
| 1766 | } |
| 1767 | // If no errors, proceed |
| 1768 | if ( $status == LATEPOINT_STATUS_SUCCESS ) { |
| 1769 | if ( OsAuthHelper::is_customer_logged_in() ) { |
| 1770 | $customer = OsAuthHelper::get_logged_in_customer(); |
| 1771 | $is_new_customer = $customer->is_new_record(); |
| 1772 | } else { |
| 1773 | $customer = new OsCustomerModel(); |
| 1774 | $is_new_customer = true; |
| 1775 | } |
| 1776 | $old_customer_data = $is_new_customer ? [] : $customer->get_data_vars(); |
| 1777 | $customer->set_data( $customer_params, LATEPOINT_PARAMS_SCOPE_PUBLIC ); |
| 1778 | if ( $customer->save() ) { |
| 1779 | if ( $is_new_customer ) { |
| 1780 | do_action( 'latepoint_customer_created', $customer ); |
| 1781 | } else { |
| 1782 | do_action( 'latepoint_customer_updated', $customer, $old_customer_data ); |
| 1783 | } |
| 1784 | |
| 1785 | self::$booking_object->customer_id = $customer->id; |
| 1786 | if ( ! OsAuthHelper::is_customer_logged_in() ) { |
| 1787 | OsAuthHelper::authorize_customer( $customer->id ); |
| 1788 | } |
| 1789 | $customer->set_timezone_name(); |
| 1790 | } else { |
| 1791 | $status = LATEPOINT_STATUS_ERROR; |
| 1792 | $response_html = $customer->get_error_messages(); |
| 1793 | if ( is_array( $response_html ) ) { |
| 1794 | $response_html = implode( ', ', $response_html ); |
| 1795 | } |
| 1796 | } |
| 1797 | } |
| 1798 | if ( $status == LATEPOINT_STATUS_ERROR ) { |
| 1799 | return new WP_Error( LATEPOINT_STATUS_ERROR, $response_html ); |
| 1800 | } |
| 1801 | |
| 1802 | } |
| 1803 | |
| 1804 | |
| 1805 | // VERIFICATION STEP |
| 1806 | |
| 1807 | public static function process_step_verify() { |
| 1808 | |
| 1809 | } |
| 1810 | |
| 1811 | public static function prepare_step_verify() { |
| 1812 | $cart = OsCartsHelper::get_or_create_cart(); |
| 1813 | |
| 1814 | $cart->set_singular_payment_attributes(); |
| 1815 | |
| 1816 | self::$vars_for_view['cart'] = $cart; |
| 1817 | self::$vars_for_view['customer'] = OsAuthHelper::get_logged_in_customer(); |
| 1818 | self::$vars_for_view['default_fields_for_customer'] = OsSettingsHelper::get_default_fields_for_customer(); |
| 1819 | } |
| 1820 | |
| 1821 | // PAYMENT |
| 1822 | |
| 1823 | public static function process_step_payment__portions() { |
| 1824 | } |
| 1825 | |
| 1826 | public static function prepare_step_payment__portions() { |
| 1827 | } |
| 1828 | |
| 1829 | public static function process_step_payment__times() { |
| 1830 | } |
| 1831 | |
| 1832 | public static function prepare_step_payment__times() { |
| 1833 | $enabled_payment_times = OsPaymentsHelper::get_enabled_payment_times(); |
| 1834 | |
| 1835 | self::$vars_for_view['enabled_payment_times'] = $enabled_payment_times; |
| 1836 | } |
| 1837 | |
| 1838 | public static function process_step_payment__methods() { |
| 1839 | } |
| 1840 | |
| 1841 | public static function prepare_step_payment__methods() { |
| 1842 | $enabled_payment_methods = OsPaymentsHelper::get_enabled_payment_methods_for_payment_time( self::$cart_object->payment_time ); |
| 1843 | self::$vars_for_view['enabled_payment_methods'] = $enabled_payment_methods; |
| 1844 | } |
| 1845 | |
| 1846 | public static function process_step_payment__processors() { |
| 1847 | } |
| 1848 | |
| 1849 | public static function prepare_step_payment__processors() { |
| 1850 | $enabled_payment_processors = OsPaymentsHelper::get_enabled_payment_processors(); |
| 1851 | self::$vars_for_view['enabled_payment_processors'] = $enabled_payment_processors; |
| 1852 | } |
| 1853 | |
| 1854 | public static function process_step_payment__pay() { |
| 1855 | } |
| 1856 | |
| 1857 | public static function prepare_step_payment__pay() { |
| 1858 | $booking_form_page_url = self::$params['booking_form_page_url'] ?? wp_get_original_referer(); |
| 1859 | $order_intent = OsOrderIntentHelper::create_or_update_order_intent( self::$cart_object, self::$restrictions, self::$presets, $booking_form_page_url ); |
| 1860 | } |
| 1861 | |
| 1862 | |
| 1863 | // CONFIRMATION |
| 1864 | |
| 1865 | public static function process_step_confirmation() { |
| 1866 | } |
| 1867 | |
| 1868 | public static function prepare_step_confirmation() { |
| 1869 | self::$vars_for_view['customer'] = OsAuthHelper::get_logged_in_customer(); |
| 1870 | self::$vars_for_view['default_fields_for_customer'] = OsSettingsHelper::get_default_fields_for_customer(); |
| 1871 | if ( ! self::$order_object->is_new_record() ) { |
| 1872 | self::$vars_for_view['order'] = self::$order_object; |
| 1873 | self::$vars_for_view['order_bookings'] = self::$order_object->get_bookings_from_order_items(); |
| 1874 | self::$vars_for_view['order_bundles'] = self::$order_object->get_bundles_from_order_items(); |
| 1875 | self::$vars_for_view['price_breakdown_rows'] = self::$order_object->generate_price_breakdown_rows(); |
| 1876 | self::$vars_for_view['is_bundle_scheduling'] = false; |
| 1877 | } else { |
| 1878 | // TRY SAVING BOOKING |
| 1879 | // check if it's a scheduling request for an existing order item, it means its a bundle |
| 1880 | $is_bundle_scheduling = self::is_bundle_scheduling(); |
| 1881 | self::$vars_for_view['is_bundle_scheduling'] = $is_bundle_scheduling; |
| 1882 | if ( $is_bundle_scheduling ) { |
| 1883 | $order_item = new OsOrderItemModel( self::$booking_object->order_item_id ); |
| 1884 | $order = new OsOrderModel( $order_item->order_id ); |
| 1885 | self::$vars_for_view['order'] = $order; |
| 1886 | self::$vars_for_view['order_bookings'] = $order->get_bookings_from_order_items(); |
| 1887 | self::$vars_for_view['order_bundles'] = $order->get_bundles_from_order_items(); |
| 1888 | self::$vars_for_view['price_breakdown_rows'] = self::$cart_object->generate_price_breakdown_rows(); |
| 1889 | |
| 1890 | if ( self::$booking_object->is_bookable() ) { |
| 1891 | self::$booking_object->calculate_end_time(); |
| 1892 | self::$booking_object->calculate_end_date(); |
| 1893 | self::$booking_object->set_utc_datetimes(); |
| 1894 | $service = new OsServiceModel( self::$booking_object->service_id ); |
| 1895 | self::$booking_object->buffer_before = $service->buffer_before; |
| 1896 | self::$booking_object->buffer_after = $service->buffer_after; |
| 1897 | |
| 1898 | if ( self::$booking_object->save() ) { |
| 1899 | do_action( 'latepoint_booking_created', self::$booking_object ); |
| 1900 | } else { |
| 1901 | // error saving booking |
| 1902 | self::$booking_object->add_error( 'booking_error', self::$booking_object->get_error_messages() ); |
| 1903 | } |
| 1904 | } else { |
| 1905 | // is not bookable |
| 1906 | self::$booking_object->add_error( 'booking_error', self::$booking_object->get_error_messages() ); |
| 1907 | } |
| 1908 | |
| 1909 | } else { |
| 1910 | $order_intent = OsOrderIntentHelper::create_or_update_order_intent( self::$cart_object, self::$restrictions, self::$presets ); |
| 1911 | if ( $order_intent->is_processing() ) { |
| 1912 | return new WP_Error( LATEPOINT_STATUS_ERROR, __( 'Processing...', 'latepoint' ), [ 'send_to_step' => 'resubmit' ] ); |
| 1913 | } |
| 1914 | if ( $order_intent->convert_to_order() ) { |
| 1915 | $order = new OsOrderModel( $order_intent->order_id ); |
| 1916 | self::$cart_object->clear(); |
| 1917 | self::$vars_for_view['order'] = $order; |
| 1918 | self::$vars_for_view['order_bookings'] = $order->get_bookings_from_order_items(); |
| 1919 | self::$vars_for_view['order_bundles'] = $order->get_bundles_from_order_items(); |
| 1920 | self::$vars_for_view['price_breakdown_rows'] = $order->generate_price_breakdown_rows(); |
| 1921 | } else { |
| 1922 | // ERROR CONVERTING TO ORDER |
| 1923 | OsDebugHelper::log( 'Error saving order', 'order_error', $order_intent->get_error_messages() ); |
| 1924 | $response_html = $order_intent->get_error_messages(); |
| 1925 | $error_data = ( $order_intent->get_error_data( 'send_to_step' ) ) ? [ 'send_to_step' => $order_intent->get_error_data( 'send_to_step' ) ] : ''; |
| 1926 | |
| 1927 | return new WP_Error( LATEPOINT_STATUS_ERROR, $response_html, $error_data ); |
| 1928 | } |
| 1929 | } |
| 1930 | } |
| 1931 | } |
| 1932 | |
| 1933 | public static function output_list_option( $option ) { |
| 1934 | $html = ''; |
| 1935 | $html .= '<div tabindex="0" class="lp-option ' . esc_attr( $option['css_class'] ) . '" ' . $option['attrs'] . '>'; |
| 1936 | $html .= '<div class="lp-option-image-w"><div class="lp-option-image" style="background-image: url(' . esc_url( $option['image_url'] ) . ')"></div></div>'; |
| 1937 | $html .= '<div class="lp-option-label">' . esc_html( $option['label'] ) . '</div>'; |
| 1938 | $html .= '</div>'; |
| 1939 | |
| 1940 | return $html; |
| 1941 | } |
| 1942 | |
| 1943 | public static function get_steps_for_select(): array { |
| 1944 | $steps = self::get_step_codes_in_order(); |
| 1945 | $steps_with_labels = []; |
| 1946 | foreach ( $steps as $step_code ) { |
| 1947 | $steps_with_labels[ $step_code ] = self::get_step_label_by_code( $step_code ); |
| 1948 | } |
| 1949 | |
| 1950 | return $steps_with_labels; |
| 1951 | } |
| 1952 | |
| 1953 | |
| 1954 | public static function save_step_codes_in_order( array $step_codes_in_order ): bool { |
| 1955 | return OsSettingsHelper::save_setting_by_name( 'step_codes_in_order', implode( ',', $step_codes_in_order ) ); |
| 1956 | } |
| 1957 | |
| 1958 | |
| 1959 | public static function save_steps_settings( $steps_settings ): bool { |
| 1960 | self::$steps_settings = $steps_settings; |
| 1961 | |
| 1962 | return OsSettingsHelper::save_setting_by_name( 'steps_settings', self::$steps_settings ); |
| 1963 | } |
| 1964 | |
| 1965 | |
| 1966 | public static function get_step_settings( string $step_code ): array { |
| 1967 | $settings = self::get_steps_settings(); |
| 1968 | |
| 1969 | return $settings[ $step_code ] ?? []; |
| 1970 | } |
| 1971 | |
| 1972 | public static function get_steps_settings(): array { |
| 1973 | if ( ! empty( self::$steps_settings ) ) { |
| 1974 | return self::$steps_settings; |
| 1975 | } |
| 1976 | |
| 1977 | $steps_settings_from_db = OsSettingsHelper::get_settings_value( 'steps_settings', [] ); |
| 1978 | $step_codes = self::get_step_codes_in_order(); |
| 1979 | |
| 1980 | |
| 1981 | if ( empty( $steps_settings_from_db ) ) { |
| 1982 | $steps_settings = [ |
| 1983 | 'shared' => [ |
| 1984 | 'steps_support_text' => '<h5>Questions?</h5><p>Call (858) 939-3746 for help</p>' |
| 1985 | ] |
| 1986 | ]; |
| 1987 | foreach ( $step_codes as $step_code ) { |
| 1988 | $steps_settings[ $step_code ] = self::get_default_value_for_step_settings( $step_code ); |
| 1989 | } |
| 1990 | OsSettingsHelper::save_setting_by_name( 'steps_settings', $steps_settings ); |
| 1991 | self::$steps_settings = $steps_settings; |
| 1992 | } else { |
| 1993 | // iterate step codes to see if each has a setting |
| 1994 | $changed = false; |
| 1995 | foreach ( $step_codes as $step_code ) { |
| 1996 | if ( ! isset( $steps_settings_from_db[ $step_code ] ) ) { |
| 1997 | $steps_settings_from_db[ $step_code ] = self::get_default_value_for_step_settings( $step_code ); |
| 1998 | $changed = true; |
| 1999 | } |
| 2000 | } |
| 2001 | if ( $changed ) { |
| 2002 | OsSettingsHelper::save_setting_by_name( 'steps_settings', $steps_settings_from_db ); |
| 2003 | } |
| 2004 | self::$steps_settings = $steps_settings_from_db; |
| 2005 | } |
| 2006 | |
| 2007 | return self::$steps_settings; |
| 2008 | } |
| 2009 | |
| 2010 | /** |
| 2011 | * @param string $step_code |
| 2012 | * @param string $placement before, after |
| 2013 | * |
| 2014 | * @return string |
| 2015 | */ |
| 2016 | public static function get_formatted_extra_step_content( string $step_code, string $placement ): string { |
| 2017 | $content = self::get_step_setting_value( $step_code, 'main_panel_content_' . $placement ); |
| 2018 | |
| 2019 | return ! empty( $content ) ? '<div class="latepoint-step-content-text-left">' . $content . '</div>' : ''; |
| 2020 | } |
| 2021 | |
| 2022 | |
| 2023 | public static function get_step_setting_value( string $step_code, string $setting_key, $default = '' ) { |
| 2024 | $steps_settings = self::get_step_settings( $step_code ); |
| 2025 | |
| 2026 | return $steps_settings[ $setting_key ] ?? $default; |
| 2027 | } |
| 2028 | |
| 2029 | public static function get_step_settings_edit_form_html( string $selected_step_code ): string { |
| 2030 | $step_settings_html = ''; |
| 2031 | switch ( $selected_step_code ) { |
| 2032 | case 'booking__services': |
| 2033 | $step_settings_html .= OsFormHelper::toggler_field( 'settings[steps_show_service_categories]', __( 'Show service categories', 'latepoint' ), OsSettingsHelper::steps_show_service_categories(), false, false, [ 'sub_label' => __( 'If turned on, services will be displayed in categories', 'latepoint' ) ] ); |
| 2034 | break; |
| 2035 | case 'booking__agents': |
| 2036 | $step_settings_html .= OsFormHelper::toggler_field( 'settings[steps_show_agent_bio]', __( 'Show Learn More about agents', 'latepoint' ), OsSettingsHelper::is_on( 'steps_show_agent_bio' ), false, false, [ 'sub_label' => __( 'A link to open information about agent will be added to each agent tile', 'latepoint' ) ] ); |
| 2037 | $step_settings_html .= OsFormHelper::toggler_field( 'settings[steps_hide_agent_info]', __( 'Hide agent name from summary and confirmation', 'latepoint' ), OsSettingsHelper::is_on( 'steps_hide_agent_info' ), false, false, [ 'sub_label' => __( 'Check if you want to hide agent name from showing up', 'latepoint' ) ] ); |
| 2038 | $step_settings_html .= OsFormHelper::toggler_field( 'settings[allow_any_agent]', __( 'Add "Any Agent" option to agent selection', 'latepoint' ), OsSettingsHelper::is_on( 'allow_any_agent' ), 'lp-any-agent-settings', false, [ 'sub_label' => __( 'Customers can pick "Any agent" and system will find a matching agent', 'latepoint' ) ] ); |
| 2039 | $step_settings_html .= '<div class="control-under-toggler" id="lp-any-agent-settings" ' . ( OsSettingsHelper::is_on( 'allow_any_agent' ) ? '' : 'style="display: none;"' ) . '>'; |
| 2040 | $step_settings_html .= OsFormHelper::select_field( 'settings[any_agent_order]', __( 'If "Any Agent" is selected then assign booking to', 'latepoint' ), OsSettingsHelper::get_order_types_list_for_any_agent_logic(), OsSettingsHelper::get_any_agent_order() ); |
| 2041 | $step_settings_html .= '</div>'; |
| 2042 | break; |
| 2043 | case 'booking__datepicker': |
| 2044 | $step_settings_html .= OsFormHelper::select_field( 'steps_settings[booking__datepicker][time_pick_style]', __( 'Show Time Slots as', 'latepoint' ), [ |
| 2045 | 'timebox' => 'Time Boxes', |
| 2046 | 'timeline' => 'Timeline' |
| 2047 | ], OsStepsHelper::get_time_pick_style() ); |
| 2048 | $step_settings_html .= OsFormHelper::toggler_field( 'steps_settings[booking__datepicker][hide_timepicker_when_one_slot_available]', __( 'Hide time picker if single slot', 'latepoint' ), OsUtilHelper::is_on( self::get_step_setting_value( $selected_step_code, 'hide_timepicker_when_one_slot_available' ) ), false, false, [ 'sub_label' => __( 'If a single slot is available in a day, it will be preselected.', 'latepoint' ) ] ); |
| 2049 | $step_settings_html .= OsFormHelper::toggler_field( 'steps_settings[booking__datepicker][hide_slot_availability_count]', __( 'Hide slot availability count', 'latepoint' ), OsStepsHelper::hide_slot_availability_count(), false, false, [ 'sub_label' => __( 'Slot counter tooltip will not appear when hovering a day.', 'latepoint' ) ] ); |
| 2050 | $step_settings_html .= OsFormHelper::toggler_field( 'steps_settings[booking__datepicker][hide_unavailable_slots]', __( 'Hide slots that are not available (time boxes)', 'latepoint' ), OsStepsHelper::hide_unavailable_slots(), false, false, [ 'sub_label' => __( 'Hides time boxes that are not available, instead of showing them in gray.', 'latepoint' ) ] ); |
| 2051 | break; |
| 2052 | case 'confirmation': |
| 2053 | $step_settings_html .= OsFormHelper::select_field( 'steps_settings[confirmation][order_confirmation_message_style]', __( 'Message Style', 'latepoint' ), ['green' => __('Green', 'latepoint'), 'yellow' => __('Yellow', 'latepoint')], self::get_step_setting_value($selected_step_code, 'order_confirmation_message_style', 'green') ); |
| 2054 | break; |
| 2055 | } |
| 2056 | /** |
| 2057 | * Generates HTML for step settings form in the preview |
| 2058 | * |
| 2059 | * @param {string} $step_settings_html html that is going to be output on the step settings form |
| 2060 | * @param {string} $selected_step_code step code that settings are requested for |
| 2061 | * @returns {string} $step_settings_html Filtered HTML of the settings form |
| 2062 | * |
| 2063 | * @since 5.0.0 |
| 2064 | * @hook latepoint_get_step_settings_edit_form_html |
| 2065 | * |
| 2066 | */ |
| 2067 | $step_settings_html = apply_filters( 'latepoint_get_step_settings_edit_form_html', $step_settings_html, $selected_step_code ); |
| 2068 | if ( empty( $step_settings_html ) ) { |
| 2069 | $step_settings_html = '<div class="bf-step-no-settings-message">' . __( 'This step does not have any specific settings. You can use the selector above to check another step.', 'latepoint' ) . '</div>'; |
| 2070 | } |
| 2071 | |
| 2072 | return $step_settings_html; |
| 2073 | } |
| 2074 | |
| 2075 | public static function get_default_value_for_step_settings( string $step_code ): array { |
| 2076 | $settings = [ |
| 2077 | 'booking__services' => [ |
| 2078 | 'side_panel_heading' => 'Service Selection', |
| 2079 | 'side_panel_description' => 'Please select a service for which you want to schedule an appointment', |
| 2080 | 'main_panel_heading' => 'Available Services' |
| 2081 | ], |
| 2082 | 'booking__locations' => [ |
| 2083 | 'side_panel_heading' => 'Location Selection', |
| 2084 | 'side_panel_description' => 'Please select a location where you want to schedule an appointment', |
| 2085 | 'main_panel_heading' => 'Available Locations' |
| 2086 | ], |
| 2087 | 'booking__agents' => [ |
| 2088 | 'side_panel_heading' => 'Agent Selection', |
| 2089 | 'side_panel_description' => 'Please select an agent that will be providing you a service', |
| 2090 | 'main_panel_heading' => 'Available Agents' |
| 2091 | ], |
| 2092 | 'booking__datepicker' => [ |
| 2093 | 'side_panel_heading' => 'Select Date & Time', |
| 2094 | 'side_panel_description' => 'Please select date and time for your appointment', |
| 2095 | 'main_panel_heading' => 'Date & Time Selection' |
| 2096 | ], |
| 2097 | 'customer' => [ |
| 2098 | 'side_panel_heading' => 'Enter Your Information', |
| 2099 | 'side_panel_description' => 'Please enter your contact information', |
| 2100 | 'main_panel_heading' => 'Customer Information' |
| 2101 | ], |
| 2102 | 'verify' => [ |
| 2103 | 'side_panel_heading' => 'Verify Order Details', |
| 2104 | 'side_panel_description' => 'Double check your reservation details and click submit button if everything is correct', |
| 2105 | 'main_panel_heading' => 'Verify Order Details', |
| 2106 | ], |
| 2107 | 'payment__times' => [ |
| 2108 | 'side_panel_heading' => 'Payment Time Selection', |
| 2109 | 'side_panel_description' => 'Please choose when you would like to pay for your appointment', |
| 2110 | 'main_panel_heading' => 'When would you like to pay?' |
| 2111 | ], |
| 2112 | 'payment__portions' => [ |
| 2113 | 'side_panel_heading' => 'Payment Portion Selection', |
| 2114 | 'side_panel_description' => 'Please select how much you would like to pay now', |
| 2115 | 'main_panel_heading' => 'How much would you like to pay now?' |
| 2116 | ], |
| 2117 | 'payment__methods' => [ |
| 2118 | 'side_panel_heading' => 'Payment Method Selection', |
| 2119 | 'side_panel_description' => 'Please select a payment method you would like to make a payment with', |
| 2120 | 'main_panel_heading' => 'Select payment method' |
| 2121 | ], |
| 2122 | 'payment__processors' => [ |
| 2123 | 'side_panel_heading' => 'Payment Processor Selection', |
| 2124 | 'side_panel_description' => 'Please select a payment processor you want to process the payment with', |
| 2125 | 'main_panel_heading' => 'Select payment processor' |
| 2126 | ], |
| 2127 | 'payment__pay' => [ |
| 2128 | 'side_panel_heading' => 'Make a Payment', |
| 2129 | 'side_panel_description' => 'Please enter your payment information so we can process the payment', |
| 2130 | 'main_panel_heading' => 'Enter your payment information' |
| 2131 | ], |
| 2132 | 'confirmation' => [ |
| 2133 | 'side_panel_heading' => 'Confirmation', |
| 2134 | 'side_panel_description' => 'Your order has been placed. Please retain this confirmation for your record.', |
| 2135 | 'main_panel_heading' => 'Order Confirmation' |
| 2136 | ] |
| 2137 | ]; |
| 2138 | |
| 2139 | |
| 2140 | $settings = apply_filters( 'latepoint_settings_for_step_codes', $settings ); |
| 2141 | |
| 2142 | return $settings[ $step_code ] ?? []; |
| 2143 | } |
| 2144 | |
| 2145 | |
| 2146 | public static function get_default_side_panel_image_html_for_step_code( string $step_code ): string { |
| 2147 | $svg = ''; |
| 2148 | switch ( $step_code ) { |
| 2149 | case 'booking__locations': |
| 2150 | $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 73 73"> |
| 2151 | <path class="latepoint-step-svg-highlight" d="M60.3884583,4.85921c-2.8716431-0.2993164-5.8259277,0.557373-7.9927979,2.197998 c-1.0095825,0.6467285-1.8696899,1.4177246-2.4382935,2.2561035c-1.7146873,2.5291042-2.5220757,6.3280535-1.3348999,10.835206 c-5.2646828-1.1404552-4.7828903-1.0880737-4.9659424-1.052002l-2.1259766,0.4560547 c-18.4231091,3.9559402-16.4117718,3.5059223-16.6292133,3.5698242 C4.8973494,18.9566498,6.1634111,19.1396389,5.8543382,19.2293282c0.0001221-0.0048828,0.0001221-0.0097656,0.0002441-0.0146484 c-0.0184326,0.012207-0.0371094,0.0292969-0.055603,0.0419922c-0.2596664,0.100153-0.2317972,0.1285801-0.3178711,0.2409668 c-0.388855,0.3278809-0.7800293,0.7553711-1.1567383,1.2041016c-0.3962412,0.4718437-0.1706734-1.9064941,0.5690308,41.3483887 c0.0057373,0.3037109,0.1334229,0.597168,0.3482666,0.8115234c0.3456421,0.3449707,0.5272217,0.5529785,0.7957764,0.7592773 c0.0950928,0.2109375,0.2803345,0.3754883,0.5170288,0.4277306c20.0937347,4.4312515,18.6302357,4.2767105,19.0541992,3.9326172 c0.0049438-0.0039063,0.0066528-0.010498,0.0114746-0.0146484c0.10186-0.0230865,15.3084774-3.4694977,17.9484882-4.0644493 c0.0352173-0.0078125,0.0643921-0.0273438,0.0973511-0.0397949c19.0996971,4.4957237,18.2303658,4.3366661,18.4299927,4.3366661 c0.4144669,0,0.7473717-0.3352814,0.75-0.7451172c0.0791321-12.2700005,0.2286911-24.8520088,0.3359375-36.9809532 c3.2604828-5.2970676,7.2790756-13.97159,5.0361328-19.7866211C67.0105286,7.553546,63.8635559,5.2127256,60.3884583,4.85921z M24.2595501,66.4368439c-0.1054153-0.0233917-14.3338861-3.1805725-16.8095703-3.727047 C7.0617967,48.3806953,6.8420701,33.9500313,6.8132615,20.8670235c5.8759589,1.233469,11.3363876,2.3809967,17.2407227,3.6113281 C24.3160305,51.6952362,24.2979584,58.1465149,24.2595501,66.4368439z M42.6662903,62.5681953 c-2.7329216,0.6163788-16.6759109,3.7770119-16.7893696,3.8027306c-0.1231174-12.0390549-0.0782604-29.8359985-0.02948-41.9248009 c5.5739422-1.1885509,11.055666-2.3654537,17.2197285-3.6884766C43.0675392,20.8666286,42.96418,48.7001991,42.6662903,62.5681953z M61.3523254,66.5017853c-5.4633789-1.2939453-11.2871094-2.6728477-16.8710938-3.989254 c-0.1817551-17.4268951-0.0330315-7.6905823,0.1430664-41.7041016c1.5129585,0.33918,2.9774971,0.6543026,4.5148926,0.9870605 c1.2711296,3.5923672,4.1154442,8.24547,6.2368164,10.9348145c0.510498,0.6472168,1.4362793,1.4404297,2.2056885,1.7519531 c0.8912773,0.6281052,1.8476524,0.4962959,2.5943604-0.1904297c0.5303345-0.4863281,1.022644-1.03125,1.4845581-1.6137695 C61.5390205,45.8931503,61.4254494,55.6076279,61.3523254,66.5017853z M64.0022278,25.9051094 c-1.2943535,2.4604969-2.8116989,5.4206085-4.840332,7.28125c-0.1386719,0.1279297-0.296875,0.1855469-0.4130859,0.2011719 c-0.7806473-0.0199814-5.2463379-5.6790333-7.6728516-13.1708984c-0.5771484-1.7861328-1.190918-4.1210938-0.8085938-6.3457041 c0.3496094-2.03125,0.9931641-3.5849609,1.9125977-4.6152344c1.8496094-2.0751953,5.0126953-3.2119141,8.0566406-2.9042969 c2.9272461,0.2978516,5.5722656,2.2568359,6.5820313,4.8740234C68.454361,15.4667559,66.1138763,21.8956394,64.0022278,25.9051094z "/> |
| 2152 | <path class="latepoint-step-svg-base" d="M54.1091614,12.0506163c-2.088459,3.2326937,0.0606689,7.85254,4.3237305,7.85254 c3.6078873,0,5.8475189-3.5880222,4.8115234-6.6953135C61.9358063,9.2799187,56.3691139,8.5516081,54.1091614,12.0506163z M58.170929,18.3797188c-0.8803711-0.0610352-1.743103-0.4106445-2.3566895-1.0410156 c-1.1245117-1.1542969-1.3198242-3.1201181-0.4453125-4.4736338c0.8155251-1.2618265,2.428051-1.8824129,4.0743408-1.404541 c0.5652466,0.5754395,1.0892944,1.170166,1.3425903,1.8354492C61.5309181,15.2528019,60.553997,17.7360039,58.170929,18.3797188z" /></svg>'; |
| 2153 | break; |
| 2154 | case 'booking__services': |
| 2155 | $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 73 73"> |
| 2156 | <path class="latepoint-step-svg-highlight" d="M12.4475956,46.2568436c-0.1044884,1.7254677-0.2875328,2.2941246,0.1235962,3.2275391 c0.2800293,1.0578613,1.2532349,2.0065918,2.4077148,2.4970703c2.5679932,1.0912819,3.8084583,0.576416,36.5757446,0.7905273 c1.5809326,0.0102539,4.2476807-0.1374512,5.786499-0.4538574c2.1460648-0.4416046,4.1996078-1.119503,4.6765137-3.3955078 c0.1690674-0.3930664,0.2585449-0.8137207,0.2453613-1.244873c-0.0195313-0.6503906-0.0566406-1.3046875-0.1044922-1.9511719 c-0.1210938-1.6845703-1.6621094-2.9892578-3.5175781-2.9892578c-0.015625,0-0.03125,0-0.046875,0l-42.6777344,0.5214844 C14.0725956,43.2812576,12.5491581,44.5976639,12.4475956,46.2568436z M58.6409569,44.2373123 c1.0712891,0,1.9560547,0.6972656,2.0214844,1.5976563c0.0458984,0.6259766,0.0830078,1.2587891,0.1005859,1.8876953 c0.0309868,1.0110512-0.9663086,1.7237892-2.0117188,1.7304688c-14.3534698,0.0823135-28.739151,0.728199-42.9609375,0.5419922 c-1.0929708-0.0137672-2.0631294-0.8028984-1.9785156-1.8085938c0.0527344-0.6113281,0.0957031-1.2294922,0.1337891-1.8378906 c0.0537109-0.8789063,0.9267578-1.5771484,1.9882813-1.5898438C16.0340576,44.757576,58.7426338,44.2373123,58.6409569,44.2373123z "/> |
| 2157 | <path class="latepoint-step-svg-base" d="M58.2141991,6.9736419l-0.5214844,4.9931645c-0.0457916,0.4391737,0.2963982,0.828125,0.7470703,0.828125 c0.3789063,0,0.7050781-0.2861328,0.7451172-0.671875l0.5214844-4.9931645 c0.0429688-0.4121094-0.2558594-0.78125-0.6679688-0.8242188C58.6360741,6.256845,58.2571678,6.5605559,58.2141991,6.9736419z"/> |
| 2158 | <path class="latepoint-step-svg-base" d="M65.2903671,8.9316502l-3.6796837,3.6767578c-0.4748344,0.4748325-0.1306915,1.2802734,0.5302734,1.2802734 c0.1914063,0,0.3837891-0.0732422,0.5302734-0.2197266L66.350914,9.992197c0.2929688-0.2929688,0.2929688-0.7675781,0-1.0605469 C66.0589218,8.639658,65.5843124,8.6377048,65.2903671,8.9316502z"/> |
| 2159 | <path class="latepoint-step-svg-base" d="M68.8108749,16.1767673c-0.1835938-0.3710938-0.6347656-0.5234375-1.0048828-0.3388672 c-1.1025391,0.5478516-2.3320313,0.7939453-3.5585938,0.7119141c-0.4033165-0.0234375-0.770504,0.2851563-0.7978477,0.6982422 s0.2851563,0.7705078,0.6982384,0.7978516c1.4586029,0.0992756,2.9659576-0.1902256,4.3242188-0.8642578 C68.8431015,16.9970798,68.9944687,16.5468845,68.8108749,16.1767673z"/> |
| 2160 | <path class="latepoint-step-svg-highlight" d="M7.0583744,24.3901463c1.7924805,0.6647949,3.8635864,0.6894531,5.857666,0.7006836 c12.414856,0.0710449,23.6358051,0.019043,36.0507202,0.0898438c1.8114014,0.0102539,4.8669434-0.1374512,6.630127-0.4538574 c1.7630615-0.3166504,3.4486084-0.7158203,4.5030518-1.8364258c0.5599365-0.5949707,0.8862305-1.326416,0.9301758-2.0551758 c0.1284103-0.495512,0.1391678-0.7500668-0.0229492-2.7072754c-0.125988-1.5260391-1.6530342-2.9814453-3.9726563-2.9814453 L8.1350956,15.6670017c-2.0859375,0.0224609-3.7490234,1.3085938-3.8671875,2.9931641 c-0.131978,1.8722496-0.2533808,2.0809135-0.0430298,2.7998047C4.332056,22.6867771,5.5573368,23.8335056,7.0583744,24.3901463z M5.7640018,18.764658c0.0615234-0.8681641,1.1318359-1.5849609,2.3867188-1.5976563l48.8994141-0.5205078 c1.2441406-0.0126953,2.3886719,0.7070313,2.4628906,1.6044922c0.0517578,0.625,0.09375,1.2558594,0.1142578,1.8818359 c0.0375061,1.0384789-1.2411385,1.7228012-2.4140625,1.7285156c-16.2836723,0.0816097-33.0511169,0.7308216-49.2275391,0.5429688 c-1.1799021-0.0141487-2.4750004-0.7440434-2.3740234-1.8007813C5.6712284,19.9912205,5.7220097,19.3730564,5.7640018,18.764658z" /> |
| 2161 | <path class="latepoint-step-svg-highlight" d="M25.6985722,38.054451c1.9748383,1.0864716,2.6161232,0.5729103,28.2541523,0.7905273 c1.2214355,0.0102539,3.28125-0.1374512,4.4699707-0.4538574c1.6699829-0.4448471,2.8914299-1.0308228,3.4542236-2.7290039 c0.6960297-1.1023483,0.5326729-2.1277504,0.4388428-3.850584c-0.0966797-1.7070313-1.40625-3.0332031-2.9306641-3.0009766 l-32.9677734,0.5205078c-1.5166016,0.0253906-2.765625,1.3466797-2.8447266,3.0097637 c-0.0829926,1.7514267-0.3514214,2.8246078,0.5612793,4.0524902C24.4834843,37.0983963,25.0513554,37.698494,25.6985722,38.054451z M25.0706425,32.4111404c0.0419922-0.8740215,0.6445313-1.5683575,1.3710938-1.5800762l32.9667969-0.5205078 c0.0058594,0,0.0117188,0,0.0175781,0c0.7314453,0,1.3417969,0.6923828,1.3916016,1.5839844 c0.0351563,0.6289043,0.0634766,1.2646465,0.078125,1.8945293c0.0201225,0.8820457-0.556736,1.731514-1.3867188,1.7373047 c-10.9964714,0.0815811-22.1932869,0.7267456-33.1787109,0.5419922c-0.7375622-0.013092-1.4293518-0.7859573-1.3623047-1.8242188 C25.0081425,33.6347733,25.0423222,33.0185623,25.0706425,32.4111404z"/> |
| 2162 | <path class="latepoint-step-svg-highlight" d="M62.451992,63.2775955c0.5789719-1.0259094,0.4419289-1.8840179,0.3344727-3.6164551 c-0.1044922-1.6894531-1.4648438-2.9960938-3.1064453-2.9960938c-0.0146484,0-0.0302734,0-0.0449219,0l-36.3544922,0.5205078 c-1.6298828,0.0234375-2.9755859,1.3427734-3.0634766,3.0048828c-0.09375,1.795887-0.3370171,2.6628914,0.4232788,3.8208008 c0.3649292,0.8071289,1.0519409,1.5019531,1.8442383,1.8972168c2.1949348,1.0950089,3.3277054,0.5763168,31.1570454,0.7905273 c1.3469238,0.0102539,3.6184082-0.1374512,4.9293213-0.4538574C60.4500313,65.7912064,61.8896866,65.1745071,62.451992,63.2775955z M59.7708397,63.3798904c-12.1266251,0.0816307-24.4732285,0.7282944-36.5908203,0.5419922 c-0.9430161-0.0149651-1.6459942-0.8662491-1.578125-1.8183594c0.0439453-0.6103516,0.0820313-1.2265625,0.1132813-1.8339844 c0.0458984-0.8769531,0.7431641-1.5722656,1.5869141-1.5839844l36.3544922-0.5205078 c0.9013672-0.0332031,1.5761719,0.6855469,1.6328125,1.5888672c0.0390625,0.6289063,0.0693359,1.2617188,0.0859375,1.8916016 C61.4014854,62.6212692,60.6525688,63.3738251,59.7708397,63.3798904z"/> |
| 2163 | </svg>'; |
| 2164 | break; |
| 2165 | case 'booking__agents': |
| 2166 | $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 73 73"> |
| 2167 | <path class="latepoint-step-svg-base" d="M53.4534083,0.0474242671 C53.0666895,-0.0961304329 52.6335841,0.0967406671 52.4866114,0.483947667 L50.3816309,6.05572497 C50.2351465,6.44342027 50.4309473,6.87603747 50.8181543,7.02252187 C51.2107248,7.16946117 51.6403055,6.96943747 51.7849512,6.58599847 L53.8899317,1.01422117 C54.0364161,0.626525867 53.8406153,0.193908667 53.4534083,0.0474242671 Z"></path> |
| 2168 | <path class="latepoint-step-svg-base" d="M55.1467677,9.54449457 L60.2917872,4.91949457 C60.5998927,4.64263907 60.624795,4.16851797 60.3479395,3.86041257 C60.0701075,3.55181877 59.5964747,3.52691647 59.2888575,3.80426027 L54.143838,8.42926027 C53.8357325,8.70611577 53.8108302,9.18023687 54.0876857,9.48834227 C54.3632441,9.79482267 54.8367587,9.82286737 55.1467677,9.54449457 Z"></path> |
| 2169 | <path class="latepoint-step-svg-base" d="M58.0530177,12.1817007 C58.1018458,12.5601187 58.4245997,12.8364859 58.7961818,12.8364859 C58.8279201,12.8364859 58.8601466,12.8345328 58.8923732,12.8306265 C60.810342,12.585021 62.7136623,11.9522085 64.3962795,11.0010376 C64.7566311,10.7974243 64.8840725,10.3399048 64.6799709,9.97906487 C64.4758693,9.61724847 64.0178615,9.49078357 63.6579982,9.69537347 C62.1428615,10.5518188 60.4289943,11.1211548 58.7019435,11.3423462 C58.2908106,11.3950796 58.0007716,11.7710562 58.0530177,12.1817007 Z"></path> |
| 2170 | <path class="latepoint-step-svg-base" d="M30.1647665,12.3430099 C34.8016087,11.2484035 39.4478623,14.1199381 40.5424644,18.7567618 C41.6370664,23.3935856 38.7655134,28.0398278 34.1286712,29.1344342 C29.491829,30.2290406 24.8455754,27.3575061 23.7509733,22.7206823 C22.6563712,18.0838585 25.5279243,13.4376163 30.1647665,12.3430099 Z M30.7048927,13.6876382 C26.8743165,14.5919117 24.5020759,18.4302508 25.406345,22.2608086 C26.3106141,26.0913663 30.1489646,28.4635885 33.9795408,27.5593151 C37.810117,26.6550416 40.1823577,22.8167025 39.2780886,18.9861448 C38.3738195,15.155587 34.535469,12.7833648 30.7048927,13.6876382 Z"></path> |
| 2171 | <path class="latepoint-step-svg-base" d="M21.9115992,61.4981718 C23.8270655,62.2352323 26.1083765,62.550601 28.0801173,62.8933134 C39.1328402,64.8145094 50.0195018,63.0462065 53.2110377,61.4772978 C54.3124781,60.935916 53.9811183,59.2539663 52.7560206,59.1805411 C50.270547,59.0314932 47.770608,59.1632071 45.3111353,59.5512114 C55.2235003,54.6875143 61.8597269,44.4488249 62.4270411,34.1118765 L62.4270411,34.1123648 C63.5544825,13.7695837 44.6203433,-0.201645833 26.3787013,3.15100097 C1.04216438,5.25931547 -5.22645982,35.1987143 4.08518218,48.907836 C7.82184888,54.4092207 14.728097,59.697505 21.9115992,61.4981718 Z M49.7043238,55.0174551 C38.1006632,64.1502943 22.8722105,61.8384047 13.4803858,53.7492056 C12.5408716,43.1234541 20.9689856,33.9107046 31.6687403,33.9107046 C42.9996081,33.9107046 51.4818011,44.1488142 49.7043238,55.0174551 Z M9.60721588,15.241271 C26.2435961,-6.79306413 62.4589091,6.43408397 60.9289942,34.029357 C60.8975687,34.1444121 60.8018961,44.9580946 51.3662501,53.6017447 C52.1936312,42.0003806 42.9873324,32.4107047 31.6687403,32.4107047 C20.7886057,32.4107047 11.8490992,41.2775069 11.9136133,52.293212 C2.00266698,42.3921652 1.59887988,25.849227 9.60721588,15.241271 Z"></path> |
| 2172 | </svg>'; |
| 2173 | break; |
| 2174 | case 'booking__datepicker': |
| 2175 | case 'customer': |
| 2176 | $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 73 73"> |
| 2177 | <path class="latepoint-step-svg-highlight" d="M36.270771,27.7026501h16.8071289c0.4140625,0,0.75-0.3359375,0.75-0.75s-0.3359375-0.75-0.75-0.75H36.270771 c-0.4140625,0-0.75,0.3359375-0.75,0.75S35.8567085,27.7026501,36.270771,27.7026501z"/> |
| 2178 | <path class="latepoint-step-svg-highlight" d="M40.5549507,42.3081207c0,0.4140625,0.3359375,0.75,0.75,0.75h12.6015625c0.4140625,0,0.75-0.3359375,0.75-0.75 s-0.3359375-0.75-0.75-0.75H41.3049507C40.8908882,41.5581207,40.5549507,41.8940582,40.5549507,42.3081207z"/> |
| 2179 | <path class="latepoint-step-svg-highlight" d="M45.6980171,51.249527H29.9778023c-0.4140625,0-0.75,0.3359375-0.75,0.75s0.3359375,0.75,0.75,0.75h15.7202148 c0.4140625,0,0.75-0.3359375,0.75-0.75S46.1120796,51.249527,45.6980171,51.249527z"/> |
| 2180 | <path class="latepoint-step-svg-highlight" d="M62.1623726,11.5883932l0.3300781-3.3564453c0.0405273-0.4121094-0.2607422-0.7792969-0.6728516-0.8193359 c-0.4091797-0.0458984-0.77882,0.2597656-0.8203125,0.6728516l-0.3300781,3.3564453 c-0.0405273,0.4121094,0.2612305,0.7792969,0.6733398,0.8193359 C61.7317963,12.3070383,62.1204109,12.0155325,62.1623726,11.5883932z"/> |
| 2181 | <path class="latepoint-step-svg-highlight" d="M63.9743843,13.9233541c1.1010704-0.3369141,2.0717735-1.0410156,2.7333946-1.9814453 c0.2382813-0.3388672,0.1567383-0.8066406-0.1816406-1.0449219c-0.3383789-0.2392578-0.8066406-0.1572266-1.0449219,0.1816406 c-0.4711914,0.6699219-1.1621094,1.1708984-1.9462852,1.4111328c-0.3959961,0.1210938-0.6186523,0.5400391-0.4975586,0.9365234 C63.1588402,13.8212023,63.5774651,14.0450754,63.9743843,13.9233541z"/> |
| 2182 | <path class="latepoint-step-svg-highlight" d="M68.8601227,17.4516735c0.0356445-0.4121094-0.2695313-0.7763672-0.6826172-0.8115234l-3.859375-0.3349609 c-0.4072227-0.0390625-0.7758751,0.2695313-0.8115196,0.6826172c-0.0356445,0.4121094,0.2695313,0.7763672,0.6826134,0.8115234 l3.859375,0.3349609C68.4594727,18.1708145,68.8244781,17.8649578,68.8601227,17.4516735z"/> |
| 2183 | <path class="latepoint-step-svg-highlight" d="M4.7497134,18.4358044c1.0574932,1.9900436,1.9738078,2.5032253,13.2814941,11.7038574 c0.5604858,11.4355488,0.9589844,22.8789082,1.1829224,34.3259277c0.3128052,0.1918945,0.6256714,0.3835449,0.9384766,0.5751953 c0.1058846,0.3764038,0.416275,0.5851364,0.7949219,0.5466309c12.6464844-1.4892578,25.8935547-2.0419922,40.4916992-1.6767578 c0.4600639-0.0021172,0.763813-0.3514481,0.7685547-0.7421875c0.1805725-16.3819695-0.080349-32.8599472,0.0605469-49.1875 c0.003418-0.3740234-0.2685547-0.6923828-0.6376953-0.7480469c-14.1435547-2.140625-28.5092773-2.3291016-42.6953125-0.5664063 c-0.331604,0.0407715-0.5751953,0.2971191-0.6331177,0.6113281c-0.3464966,0.277832-0.6930542,0.5556641-1.0396118,0.8334961 c0.1156616,1.137207,0.0985718,2.392333,0.1765137,3.5629873c-2.2901011-1.8925772-4.5957651-3.8081045-6.9354258-5.7802725 c-0.7441406-0.6269531-1.6889648-0.9277344-2.683105-0.8378906C4.4105406,11.3600969,3.320657,15.7476349,4.7497134,18.4358044z M60.7629585,14.6196432c-0.1265907,15.9033155,0.1148987,31.8954544-0.046875,47.7734375 c-14.0498047-0.3193359-26.8598633,0.2099609-39.1044922,1.6074219c0.0154419-10.8208008-0.2228394-21.3803711-0.6828613-31.503418 c8.6963615,7.0753174,9.1210613,7.5400124,10.6517334,8.1962891c2.7804565,1.1923828,7.8590698,1.5974121,8.4487305,0.6987305 c0.0741577-0.0522461,0.1495361-0.1047363,0.2015381-0.1826172c0.1469727-0.2207031,0.1669922-0.5029297,0.0517578-0.7412109 c-1.0354347-2.1505203-2.3683548-6.0868149-3.1914063-6.7568359c-5.5252628-4.5023842-10.581501-8.5776329-16.84375-13.7214375 c-0.1300049-1.973877-0.2654419-3.9484863-0.4165039-5.9221182C33.4343452,12.4419088,47.1985054,12.6274557,60.7629585,14.6196432 z M9.5368834,13.0405416c9.0454321,7.6246099,17.5216217,14.4366217,26.5917969,21.8203125 c0.3883591,0.3987503,1.5395088,3.3786926,2.2700195,5.078125c-1.4580688-0.1650391-2.9936523-0.479248-4.7089233-0.8842773 c0.4859009-0.9790039,1.1461182-1.8769531,1.953064-2.6108398c0.3061523-0.2783203,0.3286133-0.7529297,0.0498047-1.0595703 c-0.2783203-0.3046875-0.7519531-0.328125-1.0595703-0.0498047c-0.9295654,0.8461914-1.6932373,1.8774414-2.2598877,3.0026855 c-8.9527779-7.1637478-17.1909065-14.1875877-25.8739014-21.1394062c-0.5556641-0.4443359-0.8725586-1.09375-0.8481445-1.7363272 C5.7526169,12.8167362,8.1288319,11.8543167,9.5368834,13.0405416z"/> |
| 2184 | </svg>'; |
| 2185 | break; |
| 2186 | case 'payment__times': |
| 2187 | case 'payment__portions': |
| 2188 | case 'payment__methods': |
| 2189 | case 'payment__processors': |
| 2190 | case 'payment__pay': |
| 2191 | $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 73 73"> |
| 2192 | <path class="latepoint-step-svg-highlight" d="M58.6511116,6.1223307l-0.2675781,2.7509766c-0.0427284,0.4397869,0.3022537,0.8222656,0.7470703,0.8222656 c0.3818359,0,0.7080078-0.2900391,0.7451172-0.6777344l0.2675781-2.7509766 c0.0400391-0.4121094-0.2617188-0.7792969-0.6738281-0.8183594C59.0612679,5.3947916,58.6901741,5.7092447,58.6511116,6.1223307z" /> |
| 2193 | <path class="latepoint-step-svg-highlight" d="M60.9724007,11.0764322c0.296711,0.2927561,0.7712784,0.2872667,1.0605469-0.0058594 c1.0693359-1.0820313,1.8466797-2.4306641,2.2470665-3.8984375c0.109375-0.3994141-0.1269531-0.8115234-0.5263634-0.9208984 c-0.4082031-0.1083984-0.8125,0.1269531-0.9208984,0.5263672c-0.3330078,1.2197266-0.9785156,2.3398438-1.8662109,3.2382813 C60.6755257,10.3108072,60.6774788,10.7854166,60.9724007,11.0764322z"/> |
| 2194 | <path class="latepoint-step-svg-highlight" d="M68.802475,10.2619791c-0.1806641-0.3710938-0.6279297-0.5253906-1.0029297-0.3466797l-4.2695274,2.0771484 c-0.3720703,0.1816406-0.5273438,0.6308594-0.3466797,1.0029297c0.1800232,0.3695202,0.6266098,0.5278702,1.0029259,0.3466797 l4.2695313-2.0771484C68.8278503,11.0832682,68.983139,10.6340494,68.802475,10.2619791z"/> |
| 2195 | <path class="latepoint-step-svg-highlight" d="M56.075428,39.6298981l-0.0135498,0.1000977c-1.02771,0.3820801-1.6018066,1.6784668-1.2001343,2.6987305 c0.4017334,1.0202637,1.6987915,1.5778809,2.7179565,1.173584c1.019165-0.404541,1.581665-1.692627,1.1917114-2.7172852 C58.3814583,39.8601227,57.1116829,39.2714996,56.075428,39.6298981z"/> |
| 2196 | <path class="latepoint-step-svg-highlight" d="M67.1153412,64.6347809c0.3217163-0.7180176-0.0892334-1.5942383-0.7265625-2.0559082 c-0.3763428-0.2724609-0.8133545-0.4296875-1.2661743-0.5449219c0.4932785-1.2028122,0.3154755,0.6508713,0.4796753-37.815918 c0.0175247-3.8000011-0.7661972-6.7081814-4.6874352-7.2695313c-0.3728027-0.1738281-0.7583618-0.3242188-1.1530762-0.456543 c0.0695915-1.4608269-0.0228233-2.4685307-0.0032349-3.5571299c0.0311775-1.7980299-1.4539566-3.2119141-3.1962891-3.2119141 c-0.0029297,0-0.0058594,0-0.0087891,0L17.7292366,9.8449869c-3.6554623,0.0112343-7.4443989,0.1655378-10.0129395,2.8173828 c-1.4490428,1.00739-2.4756026,2.9240465-2.9685669,4.6687021c-0.8636329,3.0560856-0.6394863,1.955822-0.4553223,44.1296387 c0.0185671,4.2640686,1.1058459,5.8280563,6.0576177,5.918457c18.1763916,0.3305664,36.4078979,0.4030762,54.4744225-1.6201172 C65.7114716,65.6596832,66.750412,65.4494781,67.1153412,64.6347809z M10.1530647,12.6457682 c2.2675781-1.2832031,5.0898438-1.2929688,7.5800781-1.3007813l38.8242188-0.1220703c0.0019531,0,0.0039063,0,0.0048828,0 c0.9442444,0,1.7127266,0.7628899,1.6962891,1.6855469c-0.0167885,0.973794,0.0510406,1.9935045,0.0214844,3.1801767 c-3.1493874-0.6768255-2.4396057-0.4888554-44.4998169-0.6098642c-0.5518799-0.0014648-5.0442505,0.4206543-6.5944219,1.3168955 C7.4678226,15.1682291,8.5861702,13.5339518,10.1530647,12.6457682z M64.0123749,45.5925446l-5.2597008,0.0493164 c-3.4698677,0.0267563-7.8461227-0.6362991-7.4550781-4.0878906c0.2425804-2.1451874,2.5993347-3.0465698,4.7382813-3.3955078 c2.6318359-0.4296875,5.3945313-0.3251953,7.9882774,0.3017578c0.0061646,0.0014648,0.012085-0.0004883,0.0182495,0.0007324 L64.0123749,45.5925446z M64.0487518,36.9409332c-2.6920738-0.6071777-5.5366783-0.7060547-8.2550621-0.2629395 c-2.8740196,0.470295-5.6615906,1.8131523-5.9863281,4.7080078c-0.5018425,4.4379425,4.47435,5.7899628,8.9589844,5.7558594 l5.2397423-0.0490723c-0.0889435,13.624691,0.1381378,14.0157204-0.5004845,14.7600098 c-0.4492188,0.5253906-2.2080078,1.0888672-3.2431641,1.1425781c-17.3261032,0.8932877-33.7187004,1.8238754-50.8261719,0.8164063 c-0.8339844-0.0488281-1.4882817-0.7509766-1.4912114-1.5986328C7.9190578,52.4376526,6.8739986,19.3938637,7.102283,19.0354176 c1.2720323,0,6.8894105-0.2661171,25.2783203-0.2939453c8.4413376-0.0108852,17.2458305-0.0266666,25.7978516-0.3779297 C65.4974823,18.0765209,64.0197983,20.7003078,64.0487518,36.9409332z"/> |
| 2197 | </svg>'; |
| 2198 | break; |
| 2199 | case 'verify': |
| 2200 | $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80"> |
| 2201 | <path class="latepoint-step-svg-base" d="M14.1105938,17.6527386h21.4086933c0.4140625,0,0.75-0.3359375,0.75-0.75s-0.3359375-0.75-0.75-0.75H14.1105938 c-0.4140625,0-0.75,0.3359375-0.75,0.75S13.6965313,17.6527386,14.1105938,17.6527386z"/> |
| 2202 | <path class="latepoint-step-svg-base" d="M48.0480957,22.5179729c0.190918-4.6103516-0.2402344-8.1689453-1.3554688-11.2001953 c-1.9773369-5.3880882-10.6812592-6.6263709-16.4194965-6.88623c-2.2271042-0.3552918-3.4171219-0.4732823-23.8388062-0.9545901 C5.5955906,3.4306827,5.2978926,3.7840867,5.309813,4.2435594c0.4078836,15.8521996,0.3535037,38.6989517,0.1298828,54.6308594 c0.0489416,0.1005783,0.1066036,0.7338486,0.7416992,0.7373047c0.0014648,0,0.003418,0,0.0048828,0 c0.1726775,0,19.3874683-0.9524536,39.9575195,1.1923828c0.5861588,0.0651283,1.0673027-0.5827713,0.6965942-1.1501465 c-0.3957596-2.2545013-0.4755592-3.6757584-0.5795288-5.1481934c0.0477905-0.0227051,0.0947876-0.0480957,0.1424561-0.0710449 c2.0167389,2.6554184,8.5339165,10.8789749,11.3917847,12.6982422c0.7129517,0.4538574,1.5125732,0.8005371,2.3395996,0.9714355 c4.5379868,1.9745102,8.1917953-3.4511719,5.8001099-6.3081055c-4.0245361-4.8284912-8.767334-10.3620605-13.5692749-15.0280762 c1.0654297-2.1257324,1.6327515-4.5004883,1.6327515-6.911377c0-4.8347168-2.2924194-9.3981953-6.1298218-12.3183613 c0.0004272-0.0112305,0.0014648-0.0220947,0.0018921-0.0332031 C47.9866676,24.0398521,48.0113487,23.3549309,48.0480957,22.5179729z M45.2601929,59.2135315 c-12.4361572-1.2451172-25.3148212-1.6257324-38.3179321-1.1262207c0.02246-8.7914352,0.4327807-31.9077263-0.112915-53.0991211 c20.4045773,0.4872842,21.7616024,0.5873499,24.1508789,1.0756836c1.9755001,0.4037867,3.2904224,4.9198499,5.040041,6.5957026 c0.3312874,0.3179483,0.834362,0.2433729,1.1196289-0.0429688c1.8201218-1.8236427,4.0447845-4.2757235,6.2490234-3.3017578 c0.7670898,0.3339844,1.4047852,1.1816406,1.8959961,2.5205078c1.0449219,2.8398438,1.4467773,6.2138672,1.2641602,10.6191406 c-0.0358124,0.8280945-0.0610733,1.5315475-0.1461792,4.076416c-2.3810425-1.4171143-5.0792236-2.1643066-7.8845825-2.1643066 c-3.1671143,0-6.135437,0.9802246-8.6168232,2.6494141c-0.4119091-0.311924,0.2382946-0.0890408-15.7840576-0.3027344 c-0.0024414,0-0.0048828,0-0.0068359,0c-0.4111328,0-0.7460938,0.3310547-0.75,0.7431641 c-0.0039063,0.4140625,0.3291016,0.7529297,0.7431641,0.7568359l14.081665,0.1290283 c-2.8327827,2.5395775-5.5364246,7.2262096-5.8631592,11.064333l-10.6237793,0.2597656 c-0.4140625,0.0107422-0.7412109,0.3544922-0.7314453,0.7685547c0.0102539,0.4072266,0.34375,0.7314453,0.7495117,0.7314453 c0.0063477,0,0.0126953,0,0.019043,0l10.5239258-0.2573242c-0.0244522,3.6942863,0.6843319,7.0339737,3.2225342,10.0561523 l-11.5189209,0.1054688c-0.4140625,0.0039063-0.7470703,0.3427734-0.7431641,0.7568359 c0.0039063,0.4121094,0.3388672,0.7431641,0.75,0.7431641c0.0019531,0,0.0043945,0,0.0068359,0l12.9440308-0.1186523 c0.0007935,0.0007324,0.0015259,0.0014648,0.0023193,0.0021973c3.6866817,3.1902428,7.7025356,4.4405403,11.8752575,4.1297493 c1.9718208-0.146862,3.978672-0.6423225,6.0023689-1.4463997C44.890686,56.5292053,45.0510254,57.889801,45.2601929,59.2135315z M64.7839355,62.7582092c1.643486,1.9650421-1.8606987,5.9641113-4.7329102,3.5546875 c-0.2494545-0.2046814-7.4860306-8.2930336-12.2422485-14.1032715c1.5042725-1.1379395,2.7863159-2.5305176,3.7785034-4.102417 C56.248291,52.6703186,60.8580322,58.0475159,64.7839355,62.7582092z M52.498291,39.856842 c0,7.7039337-6.2337532,13.9804688-13.9799805,13.9804688c-7.7138691,0-13.989748-6.2714844-13.989748-13.9804688 c0-7.7516708,6.3275547-13.9902363,13.989748-13.9902363C46.3522835,25.8666058,52.498291,32.2686691,52.498291,39.856842z"/> |
| 2203 | <path class="latepoint-step-svg-base" d="M61.0549316,64.0072327c0.2964249,0.2864761,0.7709198,0.2816391,1.0605469-0.0175781 c0.2875977-0.2978516,0.2792969-0.7734375-0.0185547-1.0605469l-1.0400391-1.0039063 c-0.2978516-0.2880859-0.7734375-0.2773438-1.0605469,0.0195313c-0.2875977,0.2988281-0.2788086,0.7734375,0.0195313,1.0605469 L61.0549316,64.0072327z"/> |
| 2204 | <path class="latepoint-step-svg-base" d="M38.798584,28.5873089c-6.2089844,0-11.2602558,5.055666-11.2602558,11.2695332 c0,6.2089844,5.0512714,11.2597656,11.2602558,11.2597656c6.2009888,0,11.2597656-5.036171,11.2597656-11.2597656 C50.0583496,33.6183395,44.9775581,28.5873089,38.798584,28.5873089z M38.798584,49.6166077 c-5.3818359,0-9.7602558-4.3779297-9.7602558-9.7597656c0-5.3867188,4.3784199-9.7695332,9.7602558-9.7695332 c5.343029,0,9.7597656,4.3516827,9.7597656,9.7695332C48.5583496,45.2636604,44.1625519,49.6166077,38.798584,49.6166077z"/> |
| 2205 | <path class="latepoint-step-svg-base" d="M44.651123,39.0619202c-4.2592773-0.2041016-6.421875-0.2050781-10.8295898,0.1923828 c-0.4125977,0.0371094-0.7167969,0.4023438-0.6796875,0.8144531c0.0351563,0.3896484,0.3623047,0.6826172,0.7460938,0.6826172 c0.0229492,0,0.0454102-0.0009766,0.0683594-0.0029297c4.3188477-0.3916016,6.440918-0.3886719,10.6225586-0.1884766 c0.4106445,0.0498047,0.765625-0.2998047,0.7851563-0.7128906C45.3840332,39.4330139,45.0646973,39.0814514,44.651123,39.0619202z "/> |
| 2206 | </svg>'; |
| 2207 | break; |
| 2208 | case 'confirmation': |
| 2209 | $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80"> |
| 2210 | <path class="latepoint-step-svg-base" d="M17.6552105,33.4646034C8.2132654,33.6182289,3.8646491,39.9382057,3.773782,46.3166199 C3.6704469,53.57024,9.073472,60.8994293,18.7539654,59.3212318c0.0535278,1.8059692,0.1070557,3.6119995,0.1605835,5.4179649 c0.4868374,0.7426834,0.9158726,1.2552795,1.3218193,1.5758286c0.7646008,0.6037445,1.4473019,0.5261841,2.2800751,0.0214233 c0.9628239-0.5835876,2.1262512-1.7382126,3.8487892-3.0711861c1.3595581,1.338192,2.7954102,3.2556725,3.8725586,4.7504234 c0.6969604,0.1324463,1.3938599,0.2648926,2.0908184,0.3973389c0.354744,0.2420731,0.7306252,0.1458817,0.9553833-0.0870972 c1.1480217-1.1914139,0.2770538-0.5825653,5.0960693-4.9796104c1.381897,1.3053551,3.0732422,3.0024986,4.1270752,4.464901 c2.8935661,0.5499954,2.7743301,0.7335205,3.1699219,0.4522095c0.2846146-0.2016754,0.2662773-0.1645584,0.3554688-0.2646484 c1.3665047-1.5280838,3.0428238-3.2071915,4.854248-5.0933189c1.8391113,1.4305992,3.5415039,2.966732,5.0125732,4.6672935 c0.8833618,0.1398926,1.7667236,0.2797241,2.6500854,0.4195557c0.3787956,0.0587921,0.647274-0.1178513,0.7819214-0.3831787 c0.6037369-1.1866455,1.2043419-2.4298172,1.9224854-3.9011192c1.3636475,1.03265,2.6345825,2.1318321,3.7449989,3.3383751 c0.520752,0.0775146,0.9672852,0.0211792,1.4367676,0.0062256c0.6980667,0.5534744,1.3601151,0.1294708,1.392334-0.4434814 c1.1637878-20.9316826-0.4478302-32.0234108-1.8408203-43.4101563 c-1.0667953-8.7491531-3.4310074-16.6642761-17.6171913-18.6894531 C37.5750961,2.9660594,18.2152557,2.0518365,10.3015718,9.4919462 c-3.7495093,3.4759312-5.6556306,13.6249208-5.8579102,18.3261719c-0.0175781,0.4130859,0.3032227,0.7636719,0.7167969,0.78125 c0.0008545,0,0.0019531-0.0001831,0.0028076-0.0001831c0.0002441,0,0.0003662,0.0001831,0.0006104,0.0001831 c0.0022583,0.0003052,0.0042114-0.0008545,0.0064697-0.0005493c1.7694812,0.0453014,8.2837915-2.8392754,13.4412851-1.0584106 c0.3204956,1.9219971,0.4412842,3.8793335,0.4950562,5.8326435 C18.6154156,33.3746986,18.1323223,33.4094276,17.6552105,33.4646034z M19.1414165,57.7614784 c-7.5994434,0-11.3555832-5.7171745-11.3348923-11.4369698c0.0206909-5.7197952,3.8182158-11.4422112,11.3261032-11.4526787 c0.0092773,0,0.0180664,0,0.0273438,0c6.2543888,0,11.4311523,5.0988808,11.4311523,11.4394531 C30.5911236,52.5667496,25.5261116,57.7614784,19.1414165,57.7614784z M48.1580162,5.9938989 c13.5598068,1.9365721,15.3743439,9.4665871,16.3403358,17.3867188c0.7182922,5.8958893,3.0389252,18.635561,1.8983765,41.6446533 c-1.2305298-1.1603355-2.6870155-2.8059044-4.0233803-4.5684776c-0.3519096-0.4632568-1.1312485-0.3892365-1.3088379,0.2573853 c-0.0006714,0.0013428-0.0020142,0.0020142-0.0026855,0.0033569c-0.829628,1.6306496-1.5776443,3.2193794-2.6342773,5.3439903 c-1.9974098-2.2269859-3.4938774-3.9506302-5.3305054-5.9934654c-0.1636276-0.8107109-1.4189148-0.82724-1.5952148-0.0100098 c-1.9148636,2.1023941-4.205822,4.3376503-6.1530762,6.4651451c-1.4751854-1.9926682-3.3123169-4.1955643-4.62323-6.0411949 c-0.2008209-0.5232658-0.8574333-0.635643-1.2301025-0.258606c-2.1993942,2.222168-4.5591049,4.0396156-6.7687988,6.4904747 c-1.3328838-1.4328613-3.3396587-3.9911461-4.4924297-5.7590294c-0.2881527-0.4409218-0.9600582-0.4756927-1.2632446,0.0197754 c-1.7325058,1.1738968-2.8503933,2.218853-4.8071289,3.6727867l0.09198-5.7758751 c5.7322388-1.4144287,9.8353252-6.5934448,9.8353252-12.5602417c0-5.9226074-4.0585918-11.0758057-9.8167706-12.5380249 c-0.1152134-4.2746181-0.3553181-14.4360523-1.6055908-18.5303345c-0.6845055-2.2400188-2.8216324-5.7650404-5.5857553-7.1168213 C21.5624371,4.8990502,34.3388634,4.0191674,48.1580162,5.9938989z M6.0422945,26.9650288 c0.2917447-3.411478,1.0564828-7.6568089,2.2514648-10.9311523c0.883728-0.4779043,1.4030762-0.8288565,1.9675293-0.7024527 c0.9700317,0.2299805,1.9000244,1.0199575,2.710022,1.5799551c2.9155273,2.0056763,4.5519419,5.618042,5.333375,8.9669189 C13.8285227,24.7062149,8.9758253,26.2891541,6.0422945,26.9650288z"/> |
| 2211 | <path class="latepoint-step-svg-base" d="M20.168272,46.12183c-1.4780273-0.424263-3.6082001-0.2521667-4.2836924-1.4824219 c-0.4052734-0.7392578,0.0585938-1.7636719,0.7285166-2.2216797c0.9785156-0.6708984,2.2700195-0.5273438,2.9526367-0.3837891 c0.4052734,0.0830078,0.8032227-0.1748047,0.8886719-0.5800781s-0.1738281-0.8027344-0.5791016-0.8886719 c-0.3931274-0.0823975-0.7782593-0.130127-1.1518555-0.1454468c-0.1039429-0.53302-0.0985718-1.0831909,0.0239258-1.6152954 c0.0927734-0.4033203-0.1591797-0.8066406-0.5629883-0.8994141c-0.4038086-0.0898438-0.8061523,0.1611328-0.8989258,0.5634766 c-0.1596069,0.6945801-0.1751709,1.4108276-0.0565796,2.1081543c-0.53479,0.1254883-1.0369263,0.3114624-1.4629526,0.6027832 c-1.3994141,0.9570313-1.9360352,2.8320313-1.1962891,4.1816406c1.1052847,2.0129051,3.8100004,1.8074532,5.1850595,2.2021484 c2.1161976,0.6054153,1.8197498,2.4342194,0.3833008,3.0107422c-1.0332031,0.4150391-2.2402344,0.0205078-2.8691406-0.2519531 c-0.3808594-0.1640625-0.8217773,0.0107422-0.9863281,0.390625s0.0102539,0.8212891,0.390625,0.9863281 c0.4503174,0.1948242,1.0012817,0.3755493,1.5961304,0.4760132l0.1016235,1.6411743 c0.0249023,0.3974609,0.3549805,0.703125,0.7480469,0.703125c0.4355659,0,0.7758923-0.3669624,0.7490234-0.796875 l-0.0942383-1.5200806c0.3078613-0.0443115,0.6169434-0.112915,0.9238281-0.2357788 C23.4494343,50.8599739,23.6716747,47.1243896,20.168272,46.12183z"/> |
| 2212 | <path class="latepoint-step-svg-base" d="M27.5291119,20.7048359h28.2197247c0.4140625,0,0.75-0.3359375,0.75-0.75s-0.3359375-0.75-0.75-0.75H27.5291119 c-0.4140625,0-0.75,0.3359375-0.75,0.75S27.1150494,20.7048359,27.5291119,20.7048359z"/> |
| 2213 | <path class="latepoint-step-svg-base" d="M32.607235,31.4577656c0,0.4140625,0.3359375,0.7500019,0.75,0.7500019h23.1582031 c0.4140625,0,0.75-0.3359394,0.75-0.7500019s-0.3359375-0.75-0.75-0.75H33.357235 C32.9431725,30.7077656,32.607235,31.0437031,32.607235,31.4577656z"/> |
| 2214 | <path class="latepoint-step-svg-base" d="M55.2888756,41.443119H38.4182701c-0.4140625,0-0.75,0.3359375-0.75,0.75s0.3359375,0.75,0.75,0.75h16.8706055 c0.4140625,0,0.75-0.3359375,0.75-0.75S55.7029381,41.443119,55.2888756,41.443119z"/> |
| 2215 | </svg>'; |
| 2216 | break; |
| 2217 | } |
| 2218 | |
| 2219 | /** |
| 2220 | * Generates an SVG image for step code, if there was no custom image set |
| 2221 | * |
| 2222 | * @param {string} $svg image svg code |
| 2223 | * @param {string} $step_code step name code |
| 2224 | * |
| 2225 | * @since 5.0.0 |
| 2226 | * @hook latepoint_svg_for_step_code |
| 2227 | * |
| 2228 | */ |
| 2229 | return apply_filters( 'latepoint_svg_for_step_code', $svg, $step_code ); |
| 2230 | } |
| 2231 | |
| 2232 | |
| 2233 | public static function get_time_pick_style() { |
| 2234 | return OsStepsHelper::get_step_setting_value( 'booking__datepicker', 'time_pick_style', 'timebox' ); |
| 2235 | } |
| 2236 | |
| 2237 | /** |
| 2238 | * Generates a preview for a selected step to show on booking form preview in settings |
| 2239 | * |
| 2240 | * @param string $selected_step_code |
| 2241 | * |
| 2242 | * @return void |
| 2243 | */ |
| 2244 | public static function get_step_content_preview( string $selected_step_code ) { |
| 2245 | switch ( $selected_step_code ) { |
| 2246 | case 'booking__services': |
| 2247 | OsBookingHelper::generate_services_bundles_and_categories_list(); |
| 2248 | break; |
| 2249 | case 'booking__agents': |
| 2250 | $agents_model = new OsAgentModel(); |
| 2251 | $agents = $agents_model->should_be_active()->get_results_as_models(); |
| 2252 | OsAgentHelper::generate_agents_list( $agents ); |
| 2253 | break; |
| 2254 | case 'booking__datepicker': |
| 2255 | $booking = new OsBookingModel(); |
| 2256 | $services = new OsServiceModel(); |
| 2257 | $service = $services->should_be_active()->set_limit( 1 )->get_results_as_models(); |
| 2258 | if ( $service ) { |
| 2259 | $booking->service_id = $service->id; |
| 2260 | ?> |
| 2261 | <div class="os-dates-w" data-time-pick-style="<?php echo esc_attr(OsStepsHelper::get_time_pick_style()); ?>"> |
| 2262 | <?php OsCalendarHelper::generate_calendar_for_datepicker_step( \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ), new OsWpDateTime( 'now' ) ); ?> |
| 2263 | </div> |
| 2264 | <div class="time-selector-w <?php echo OsStepsHelper::hide_unavailable_slots() ? 'hide-not-available-slots' : ''; ?> <?php echo 'time-system-' . esc_attr(OsTimeHelper::get_time_system()); ?> <?php echo ( OsSettingsHelper::is_on( 'show_booking_end_time' ) ) ? 'with-end-time' : 'without-end-time'; ?> style-<?php echo esc_attr(OsStepsHelper::get_time_pick_style()); ?>"> |
| 2265 | <div class="times-header"> |
| 2266 | <div class="th-line"></div> |
| 2267 | <div class="times-header-label"> |
| 2268 | <?php esc_html_e( 'Pick a slot for', 'latepoint' ); ?> <span></span> |
| 2269 | <?php do_action( 'latepoint_step_datepicker_appointment_time_header_label', $booking ); ?> |
| 2270 | </div> |
| 2271 | <div class="th-line"></div> |
| 2272 | </div> |
| 2273 | <div class="os-times-w"> |
| 2274 | <div class="timeslots"></div> |
| 2275 | </div> |
| 2276 | </div> |
| 2277 | <?php |
| 2278 | } else { |
| 2279 | echo 'You need to have an active service to generate the calendar'; |
| 2280 | } |
| 2281 | break; |
| 2282 | case 'booking__locations': |
| 2283 | OsLocationHelper::generate_locations_and_categories_list(); |
| 2284 | break; |
| 2285 | case 'customer': |
| 2286 | $booking = new OsBookingModel(); |
| 2287 | $services = new OsServiceModel(); |
| 2288 | $service = $services->should_be_active()->set_limit( 1 )->get_results_as_models(); |
| 2289 | $customer = new OsCustomerModel(); |
| 2290 | $default_fields_for_customer = OsSettingsHelper::get_default_fields_for_customer(); |
| 2291 | |
| 2292 | $current_step_code = $selected_step_code; |
| 2293 | |
| 2294 | include LATEPOINT_VIEWS_ABSPATH . 'booking_form_settings/previews/_customer.php'; |
| 2295 | break; |
| 2296 | case 'payment__times': |
| 2297 | echo '<div class="booking-preview-step-skipped-message">' . esc_html__( "If you have both a payment processor and pay locally enabled, customer will make a selection here.", 'latepoint' ) . '</div>'; |
| 2298 | break; |
| 2299 | case 'payment__portions': |
| 2300 | echo '<div class="booking-preview-step-skipped-message">' . esc_html__( "If selected service has both deposit and charge amount set, customer will have to pick how much they want to pay now.", 'latepoint' ) . '</div>'; |
| 2301 | break; |
| 2302 | case 'payment__methods': |
| 2303 | echo '<div class="booking-preview-step-skipped-message">' . esc_html__( "If you have multiple payment processors enabled, customer will be able to select how they want to pay", 'latepoint' ) . '</div>'; |
| 2304 | break; |
| 2305 | case 'payment__pay': |
| 2306 | echo '<div class="booking-preview-step-skipped-message">' . esc_html__( "Payment form generated by selected payment processor will appear here", 'latepoint' ) . '</div>'; |
| 2307 | break; |
| 2308 | case 'confirmation': |
| 2309 | echo '<div class="summary-status-wrapper summary-status-style-'.esc_attr(OsStepsHelper::get_step_setting_value($selected_step_code, 'order_confirmation_message_style', 'green')).'">'; |
| 2310 | echo '<div class="summary-status-inner">'; |
| 2311 | echo '<div class="ss-icon"></div>'; |
| 2312 | echo '<div class="ss-title bf-side-heading editable-setting" data-setting-key="['. esc_attr($selected_step_code).'][order_confirmation_message_title]" contenteditable="true">'.esc_html(OsStepsHelper::get_step_setting_value($selected_step_code, 'order_confirmation_message_title', __('Appointment Confirmed', 'latepoint'))).'</div>'; |
| 2313 | echo '<div class="ss-description bf-side-heading editable-setting" data-setting-key="['. esc_attr($selected_step_code).'][order_confirmation_message_content]" contenteditable="true">'.esc_html(OsStepsHelper::get_step_setting_value($selected_step_code, 'order_confirmation_message_content', __('We look forward to seeing you.', 'latepoint'))).'</div>'; |
| 2314 | echo '<div class="ss-confirmation-number"><span>'.esc_html__('Order #', 'latepoint').'</span><strong>KDFJ934K</strong></div>'; |
| 2315 | echo '</div>'; |
| 2316 | echo '</div>'; |
| 2317 | echo '<div class="booking-preview-step-skipped-message">' . esc_html__( "Order information will appear here.", 'latepoint' ) . '</div>'; |
| 2318 | break; |
| 2319 | } |
| 2320 | do_action( 'latepoint_get_step_content_preview', $selected_step_code ); |
| 2321 | } |
| 2322 | |
| 2323 | public static function hide_slot_availability_count(): bool { |
| 2324 | return OsUtilHelper::is_on( self::get_step_setting_value( 'booking__datepicker', 'hide_slot_availability_count' ) ); |
| 2325 | } |
| 2326 | |
| 2327 | public static function hide_timepicker_when_one_slot_available(): bool { |
| 2328 | return OsUtilHelper::is_on( self::get_step_setting_value( 'booking__datepicker', 'hide_timepicker_when_one_slot_available' ) ); |
| 2329 | } |
| 2330 | |
| 2331 | public static function build_booking_object_for_current_step_preview( string $current_step ): OsBookingModel { |
| 2332 | $booking = new OsBookingModel(); |
| 2333 | $steps_in_order = self::get_step_codes_in_order(); |
| 2334 | |
| 2335 | $current_step_index = array_search( $current_step, $steps_in_order ); |
| 2336 | if ( $current_step_index === false ) { |
| 2337 | return $booking; |
| 2338 | } |
| 2339 | $completed_steps = array_slice( $steps_in_order, 0, $current_step_index ); |
| 2340 | foreach ( $completed_steps as $completed_step ) { |
| 2341 | self::set_booking_object_values_for_completed_step( $booking, $completed_step ); |
| 2342 | } |
| 2343 | |
| 2344 | return $booking; |
| 2345 | } |
| 2346 | |
| 2347 | public static function set_booking_object_values_for_completed_step( OsBookingModel $booking, string $completed_step ): OsBookingModel { |
| 2348 | switch ( $completed_step ) { |
| 2349 | case 'booking__services': |
| 2350 | $services = new OsServiceModel(); |
| 2351 | $service = $services->should_be_active()->set_limit( 1 )->get_results_as_models(); |
| 2352 | if ( $service ) { |
| 2353 | $booking->service_id = $service->id; |
| 2354 | } |
| 2355 | break; |
| 2356 | case 'booking__locations': |
| 2357 | $locations = new OsLocationModel(); |
| 2358 | $location = $locations->should_be_active()->set_limit( 1 )->get_results_as_models(); |
| 2359 | if ( $location ) { |
| 2360 | $booking->location_id = $location->id; |
| 2361 | } |
| 2362 | break; |
| 2363 | case 'booking__agents': |
| 2364 | $agents = new OsAgentModel(); |
| 2365 | $agent = $agents->should_be_active()->set_limit( 1 )->get_results_as_models(); |
| 2366 | if ( $agent ) { |
| 2367 | $booking->agent_id = $agent->id; |
| 2368 | } |
| 2369 | break; |
| 2370 | case 'customer': |
| 2371 | $customers = new OsCustomerModel(); |
| 2372 | $customer = $customers->set_limit( 1 )->get_results_as_models(); |
| 2373 | if ( $customer ) { |
| 2374 | $booking->customer_id = $customer->id; |
| 2375 | } |
| 2376 | break; |
| 2377 | case 'booking__datepicker': |
| 2378 | $tomorrow = new OsWpDateTime( 'tomorrow' ); |
| 2379 | $booking->start_date = $tomorrow->format( 'Y-m-d' ); |
| 2380 | $booking->start_time = 600; |
| 2381 | |
| 2382 | break; |
| 2383 | } |
| 2384 | |
| 2385 | /** |
| 2386 | * Sets values for booking object depending on a completed step code |
| 2387 | * |
| 2388 | * @param {OsBookingModel} $booking booking object |
| 2389 | * @param {string} $completed_step step code that was completed |
| 2390 | * |
| 2391 | * @since 5.0.0 |
| 2392 | * @hook latepoint_set_booking_object_values_for_completed_step |
| 2393 | * |
| 2394 | */ |
| 2395 | return apply_filters( 'latepoint_set_booking_object_values_for_completed_step', $booking, $completed_step ); |
| 2396 | } |
| 2397 | |
| 2398 | public static function generate_summary_key_value_pairs( OsBookingModel $booking ): string { |
| 2399 | $html = ''; |
| 2400 | |
| 2401 | |
| 2402 | if ( $booking->location_id ) { |
| 2403 | $html .= '<div class="summary-box summary-box-location-info"> |
| 2404 | <div class="summary-box-heading"> |
| 2405 | <div class="sbh-item">' . __( 'Location', 'latepoint' ) . '</div> |
| 2406 | <div class="sbh-line"></div> |
| 2407 | </div> |
| 2408 | <div class="summary-box-content with-media"> |
| 2409 | <div class="sbc-content-i"> |
| 2410 | <div class="sbc-main-item">' . $booking->location->name . '</div> |
| 2411 | </div> |
| 2412 | </div> |
| 2413 | </div>'; |
| 2414 | } |
| 2415 | if ( $booking->customer_id ) { |
| 2416 | $html .= '<div class="summary-box summary-box-customer-info"> |
| 2417 | <div class="summary-box-heading"> |
| 2418 | <div class="sbh-item">' . __( 'Customer', 'latepoint' ) . '</div> |
| 2419 | <div class="sbh-line"></div> |
| 2420 | </div> |
| 2421 | <div class="summary-box-content with-media"> |
| 2422 | <div class="os-avatar-w"> |
| 2423 | <div class="os-avatar"><span>' . esc_html( $booking->customer->get_initials() ) . '</span></div> |
| 2424 | </div> |
| 2425 | <div class="sbc-content-i"> |
| 2426 | <div class="sbc-main-item">' . esc_html( $booking->customer->full_name ) . '</div> |
| 2427 | <div class="sbc-sub-item">' . esc_html( $booking->customer->email ) . '</div> |
| 2428 | </div> |
| 2429 | </div>'; |
| 2430 | $customer_attributes = []; |
| 2431 | $customer_attributes = apply_filters( 'latepoint_booking_summary_customer_attributes', $customer_attributes, $booking->customer ); |
| 2432 | if ( $customer_attributes ) { |
| 2433 | $html .= '<div class="summary-attributes sa-clean sa-hidden">'; |
| 2434 | foreach ( $customer_attributes as $attribute ) { |
| 2435 | $html .= '<span>' . esc_html( $attribute['label'] ) . ': <strong>' . esc_html( $attribute['value'] ) . '</strong></span>'; |
| 2436 | } |
| 2437 | $html .= '</div>'; |
| 2438 | } |
| 2439 | $html .= '</div>'; |
| 2440 | } |
| 2441 | if ( OsSettingsHelper::is_off( 'steps_hide_agent_info' ) && $booking->agent_id && $booking->agent_id != LATEPOINT_ANY_AGENT ) { |
| 2442 | $bio_html = ''; |
| 2443 | if ( OsSettingsHelper::steps_show_agent_bio() ) { |
| 2444 | $bio_html .= '<div class="os-trigger-item-details-popup sbc-link-item" data-item-details-popup-id="osItemDetailsPopupAgent_' . $booking->agent_id . '">' . __( 'Learn More', 'latepoint' ) . '</div>'; |
| 2445 | $bio_html .= OsAgentHelper::generate_bio( $booking->agent ); |
| 2446 | } |
| 2447 | $html .= '<div class="summary-box summary-box-agent-info"> |
| 2448 | <div class="summary-box-heading"> |
| 2449 | <div class="sbh-item">' . __( 'Agent', 'latepoint' ) . '</div> |
| 2450 | <div class="sbh-line"></div> |
| 2451 | </div> |
| 2452 | <div class="summary-box-content with-media"> |
| 2453 | <div class="os-avatar-w" |
| 2454 | style="background-image: url(' . ( ( $booking->agent->avatar_image_id ) ? $booking->agent->get_avatar_url() : '' ) . ')"> |
| 2455 | ' . ( ( ! $booking->agent->avatar_image_id ) ? '<div class="os-avatar"><span>' . esc_html( $booking->agent->get_initials() ) . '</span></div>' : '' ) . ' |
| 2456 | </div> |
| 2457 | <div class="sbc-content-i"> |
| 2458 | <div class="sbc-main-item">' . esc_html( $booking->agent->full_name ) . '</div> |
| 2459 | ' . $bio_html . ' |
| 2460 | </div> |
| 2461 | </div> |
| 2462 | </div>'; |
| 2463 | } |
| 2464 | |
| 2465 | |
| 2466 | /** |
| 2467 | * Key value pairs of summary values for the booking summary panel |
| 2468 | * |
| 2469 | * @param {string} $html HTML of key value pairs |
| 2470 | * @param {OsBookingModel} $booking Booking object that is used to generate the summary |
| 2471 | * @returns {string} $html The filtered HTML of key value pairs |
| 2472 | * |
| 2473 | * @since 5.0.0 |
| 2474 | * @hook latepoint_summary_key_value_pairs |
| 2475 | * |
| 2476 | */ |
| 2477 | $html = apply_filters( 'latepoint_summary_key_value_pairs', $html, $booking ); |
| 2478 | |
| 2479 | if ( $html ) { |
| 2480 | $html = '<div class="summary-boxes-columns">' . $html . '</div>'; |
| 2481 | } |
| 2482 | |
| 2483 | return $html; |
| 2484 | } |
| 2485 | |
| 2486 | public static function is_ready_for_summary() { |
| 2487 | if ( ! empty( self::$order_object ) && ! self::$order_object->is_new_record() ) { |
| 2488 | // order object is set - don't need to show summary anymore |
| 2489 | return false; |
| 2490 | } |
| 2491 | if ( ! self::$cart_object->is_empty() ) { |
| 2492 | // cart has items inside - show summary |
| 2493 | return true; |
| 2494 | } |
| 2495 | if ( self::$active_cart_item->is_bundle() ) { |
| 2496 | // bundle selected already - show summary |
| 2497 | return true; |
| 2498 | } |
| 2499 | if ( ! empty( self::$booking_object->service_id ) ) { |
| 2500 | // service is selected for a booking - show summary |
| 2501 | return true; |
| 2502 | } |
| 2503 | |
| 2504 | |
| 2505 | return false; |
| 2506 | } |
| 2507 | |
| 2508 | public static function set_active_cart_item_object( array $cart_item_params = [] ): OsCartItemModel { |
| 2509 | self::$active_cart_item = new OsCartItemModel(); |
| 2510 | if ( ! empty( $cart_item_params['id'] ) ) { |
| 2511 | self::$active_cart_item->id = $cart_item_params['id']; |
| 2512 | // try to find it in cart |
| 2513 | $cart_item = new OsCartItemModel( self::$active_cart_item->id ); |
| 2514 | if ( $cart_item->is_new_record() ) { |
| 2515 | // not found, reset active cart item ID |
| 2516 | self::$active_cart_item = new OsCartItemModel(); |
| 2517 | } |
| 2518 | } |
| 2519 | self::$active_cart_item->variant = ! empty( $cart_item_params['variant'] ) ? $cart_item_params['variant'] : (empty(self::$presets['selected_bundle']) ? LATEPOINT_ITEM_VARIANT_BOOKING : LATEPOINT_ITEM_VARIANT_BUNDLE); |
| 2520 | if ( self::$active_cart_item->is_bundle() ) { |
| 2521 | if(empty($cart_item_params['item_data'])){ |
| 2522 | self::$active_cart_item->item_data = empty(self::$presets['selected_bundle']) ? '' : wp_json_encode(['bundle_id' => self::$presets['selected_bundle']]); |
| 2523 | }else{ |
| 2524 | // bundle gets data from params |
| 2525 | self::$active_cart_item->item_data = is_array( $cart_item_params['item_data'] ) ? wp_json_encode( $cart_item_params['item_data'], true ) : $cart_item_params['item_data']; |
| 2526 | } |
| 2527 | } else { |
| 2528 | // booking gets data from booking object |
| 2529 | self::$active_cart_item->item_data = wp_json_encode( self::$booking_object->generate_params_for_booking_form(), true ); |
| 2530 | } |
| 2531 | |
| 2532 | return self::$active_cart_item; |
| 2533 | } |
| 2534 | |
| 2535 | public static function get_cart_item_object() { |
| 2536 | return self::$active_cart_item; |
| 2537 | } |
| 2538 | |
| 2539 | |
| 2540 | /** |
| 2541 | * |
| 2542 | * Given a step code, returns the first sub step if found, or returns the parent step code if no children |
| 2543 | * |
| 2544 | * @param string $parent_code |
| 2545 | * |
| 2546 | * @return string |
| 2547 | */ |
| 2548 | public static function get_first_step_for_parent_code( string $parent_code ): string { |
| 2549 | $first_step_code = ''; |
| 2550 | $step_codes = self::$step_codes_in_order; |
| 2551 | foreach ( $step_codes as $step_code ) { |
| 2552 | $loop_parent_code = explode( '__', $step_code )[0]; |
| 2553 | if ( $loop_parent_code == $parent_code ) { |
| 2554 | $first_step_code = $step_code; |
| 2555 | break; |
| 2556 | } |
| 2557 | } |
| 2558 | |
| 2559 | return $first_step_code; |
| 2560 | } |
| 2561 | |
| 2562 | public static function check_step_code_access( string $step_code_to_access ): string { |
| 2563 | if ( $step_code_to_access == 'confirmation' && ! self::$order_object->is_new_record() ) { |
| 2564 | return $step_code_to_access; |
| 2565 | } |
| 2566 | // loops through all steps and checks if they satisfy condition to be skipped |
| 2567 | for ( $i = 0; $i < count( self::$step_codes_in_order ); $i ++ ) { |
| 2568 | $code = self::$step_codes_in_order[ $i ]; |
| 2569 | $parent_code = explode( '__', $code )[0]; |
| 2570 | |
| 2571 | $next_code = ( ( $i + 1 ) < count( self::$step_codes_in_order ) ) ? self::$step_codes_in_order[ $i + 1 ] : false; |
| 2572 | $next_parent_code = $next_code ? explode( '__', $next_code )[0] : false; |
| 2573 | |
| 2574 | if ( $step_code_to_access == $code ) { |
| 2575 | break; |
| 2576 | } |
| 2577 | switch ( $parent_code ) { |
| 2578 | // even tho we are checking a parent code - make sure to assign to a $code, because it's a first one in order in that parent |
| 2579 | case 'customer': |
| 2580 | if ( ! OsAuthHelper::is_customer_logged_in() ) { |
| 2581 | $step_code_to_access = $code; |
| 2582 | break 2; |
| 2583 | } |
| 2584 | break; |
| 2585 | case 'booking': |
| 2586 | if ( $next_parent_code && $next_parent_code != $parent_code && self::$cart_object->is_empty() ) { |
| 2587 | // $step_code_to_access = self::get_first_step_for_parent_code($parent_code); |
| 2588 | // break 2; |
| 2589 | } |
| 2590 | break; |
| 2591 | } |
| 2592 | } |
| 2593 | |
| 2594 | /** |
| 2595 | * Checks if a step code can be accessed, returns the step code that can be accessed |
| 2596 | * |
| 2597 | * @param {string} $step_code_to_access step code that needs to be checked for access |
| 2598 | * @returns {string} $step_code_to_access The filtered step code that can be accessed |
| 2599 | * |
| 2600 | * @since 5.0.0 |
| 2601 | * @hook latepoint_check_step_code_access |
| 2602 | * |
| 2603 | */ |
| 2604 | return apply_filters( 'latepoint_check_step_code_access', $step_code_to_access ); |
| 2605 | } |
| 2606 | |
| 2607 | public static function get_first_step_code( string $step_code, $step_codes = false ): string { |
| 2608 | if ( ! $step_codes ) { |
| 2609 | $step_codes = self::get_step_codes_in_order(); |
| 2610 | } |
| 2611 | if ( isset( $step_codes[ $step_code ] ) ) { |
| 2612 | return $step_code; |
| 2613 | } |
| 2614 | $unflat_step_codes = self::unflatten_steps( $step_codes ); |
| 2615 | |
| 2616 | // TODO add support for more than 2 dimentional parent/child arrays |
| 2617 | if ( isset( $unflat_step_codes[ $step_code ] ) ) { |
| 2618 | return implode( '__', [ $step_code, array_key_first( $unflat_step_codes[ $step_code ] ) ] ); |
| 2619 | } |
| 2620 | |
| 2621 | return ''; |
| 2622 | } |
| 2623 | |
| 2624 | public static function build_cart_object(): OsCartModel { |
| 2625 | if ( ! isset( self::$cart_object ) ) { |
| 2626 | self::set_cart_object(); |
| 2627 | } |
| 2628 | |
| 2629 | return self::$cart_object; |
| 2630 | } |
| 2631 | |
| 2632 | public static function set_order_object( array $params = [] ): OsOrderModel { |
| 2633 | self::$order_object = new OsOrderModel(); |
| 2634 | |
| 2635 | return self::$order_object; |
| 2636 | } |
| 2637 | |
| 2638 | public static function set_cart_object( array $params = [] ): OsCartModel { |
| 2639 | self::$cart_object = OsCartsHelper::get_or_create_cart(); |
| 2640 | if ( self::$cart_object->order_id ) { |
| 2641 | self::load_order_object( self::$cart_object->order_id ); |
| 2642 | } else { |
| 2643 | self::load_order_object(); |
| 2644 | self::$cart_object->set_data( $params ); |
| 2645 | |
| 2646 | // set source id |
| 2647 | if ( isset( self::$restrictions['source_id'] ) ) { |
| 2648 | self::$cart_object->source_id = self::$restrictions['source_id']; |
| 2649 | } |
| 2650 | |
| 2651 | self::$cart_object->calculate_prices(); |
| 2652 | } |
| 2653 | |
| 2654 | return self::$cart_object; |
| 2655 | } |
| 2656 | |
| 2657 | public static function set_cart_object_from_order_intent( OsOrderIntentModel $order_intent ): OsCartModel { |
| 2658 | OsCartsHelper::get_or_create_cart(); |
| 2659 | self::$cart_object->clear(); |
| 2660 | |
| 2661 | |
| 2662 | // add items from intent |
| 2663 | $intent_cart_items = json_decode( $order_intent->cart_items_data, true ); |
| 2664 | foreach ( $intent_cart_items as $cart_item_data ) { |
| 2665 | OsCartsHelper::add_item_to_cart( OsCartsHelper::create_cart_item_from_item_data( $cart_item_data ) ); |
| 2666 | } |
| 2667 | |
| 2668 | // restore payment info |
| 2669 | $payment_data = json_decode( $order_intent->payment_data, true ); |
| 2670 | self::$cart_object->payment_method = $payment_data['method']; |
| 2671 | self::$cart_object->payment_time = $payment_data['time']; |
| 2672 | self::$cart_object->payment_portion = $payment_data['portion']; |
| 2673 | self::$cart_object->payment_token = $payment_data['token']; |
| 2674 | self::$cart_object->payment_processor = $payment_data['processor']; |
| 2675 | |
| 2676 | return self::$cart_object; |
| 2677 | } |
| 2678 | |
| 2679 | public static function hide_unavailable_slots() { |
| 2680 | return OsUtilHelper::is_on( self::get_step_setting_value( 'booking__datepicker', 'hide_unavailable_slots' ) ); |
| 2681 | } |
| 2682 | } |