isset( $_POST['days_of_week'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['days_of_week'] ) ) : array(), 'start_time' => isset( $_POST['start_time'] ) ? sanitize_text_field( wp_unslash( $_POST['start_time'] ) ) : '', 'end_time' => isset( $_POST['end_time'] ) ? sanitize_text_field( wp_unslash( $_POST['end_time'] ) ) : '', 'capacity' => isset( $_POST['capacity'] ) ? absint( $_POST['capacity'] ) : 0, 'valid_from' => isset( $_POST['valid_from'] ) ? sanitize_text_field( wp_unslash( $_POST['valid_from'] ) ) : '', 'valid_until' => isset( $_POST['valid_until'] ) ? sanitize_text_field( wp_unslash( $_POST['valid_until'] ) ) : '', 'frequency' => isset( $_POST['frequency'] ) ? sanitize_key( wp_unslash( $_POST['frequency'] ) ) : 'weekly', 'kind' => isset( $_POST['kind'] ) ? sanitize_key( wp_unslash( $_POST['kind'] ) ) : '', ); $vefmb_result = $vefmb_rule_id ? $vefmb_avail_service->update_rule( $vefmb_rule_id, $vefmb_rule_input ) : $vefmb_avail_service->create_rule( $vefmb_resource_id, $vefmb_rule_input ); // Other start times typed on a new "day" rule: one more rule for each, same days, capacity and dates. $vefmb_more_starts = array(); if ( ! $vefmb_rule_id && ! is_wp_error( $vefmb_result ) && 'night' !== ( $vefmb_avail_service->get_rule( (int) $vefmb_result )['kind'] ?? 'day' ) ) { $vefmb_typed_starts = isset( $_POST['more_start_times'] ) ? preg_split( '/[\s,;]+/', sanitize_text_field( wp_unslash( $_POST['more_start_times'] ) ), -1, PREG_SPLIT_NO_EMPTY ) : array(); // "Every X minutes until …": the start times between the first one and the last one. $vefmb_every = isset( $_POST['repeat_every'] ) ? absint( $_POST['repeat_every'] ) : 0; $vefmb_last = isset( $_POST['last_start'] ) ? sanitize_text_field( wp_unslash( $_POST['last_start'] ) ) : ''; $vefmb_pause_from = isset( $_POST['pause_from'] ) ? sanitize_text_field( wp_unslash( $_POST['pause_from'] ) ) : ''; $vefmb_pause_to = isset( $_POST['pause_to'] ) ? sanitize_text_field( wp_unslash( $_POST['pause_to'] ) ) : ''; $vefmb_has_pause = preg_match( '/^([01]\d|2[0-3]):[0-5]\d$/', $vefmb_pause_from ) && preg_match( '/^([01]\d|2[0-3]):[0-5]\d$/', $vefmb_pause_to ) && $vefmb_pause_to > $vefmb_pause_from; if ( $vefmb_every >= 5 && preg_match( '/^([01]?\d|2[0-3]):[0-5]\d$/', $vefmb_last ) && preg_match( '/^([01]?\d|2[0-3]):[0-5]\d$/', $vefmb_rule_input['start_time'] ) ) { list( $vefmb_h, $vefmb_m ) = array_map( 'intval', explode( ':', $vefmb_rule_input['start_time'] ) ); list( $vefmb_lh, $vefmb_lm ) = array_map( 'intval', explode( ':', $vefmb_last ) ); $vefmb_end_min = $vefmb_lh * 60 + $vefmb_lm; for ( $vefmb_min = $vefmb_h * 60 + $vefmb_m + $vefmb_every, $vefmb_count = 0; $vefmb_min <= $vefmb_end_min && $vefmb_count < 60; $vefmb_min += $vefmb_every, ++$vefmb_count ) { $vefmb_candidate = sprintf( '%02d:%02d', intdiv( $vefmb_min, 60 ), $vefmb_min % 60 ); // A departure that falls in the break is not created. if ( $vefmb_has_pause && $vefmb_candidate >= $vefmb_pause_from && $vefmb_candidate < $vefmb_pause_to ) { continue; } $vefmb_typed_starts[] = $vefmb_candidate; } } foreach ( $vefmb_typed_starts as $vefmb_more_start ) { if ( preg_match( '/^([01]?\d|2[0-3]):[0-5]\d$/', $vefmb_more_start ) ) { $vefmb_more_starts[ str_pad( $vefmb_more_start, 5, '0', STR_PAD_LEFT ) ] = true; } } unset( $vefmb_more_starts[ $vefmb_rule_input['start_time'] ] ); foreach ( array_keys( $vefmb_more_starts ) as $vefmb_more_start ) { $vefmb_more_result = $vefmb_avail_service->create_rule( $vefmb_resource_id, array_merge( $vefmb_rule_input, array( 'start_time' => $vefmb_more_start, 'end_time' => '' ) ) ); if ( is_wp_error( $vefmb_more_result ) ) { $vefmb_result = $vefmb_more_result; break; } } } if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } elseif ( $vefmb_rule_id ) { $vefmb_notice = __( 'Rule updated. Slots already generated are unchanged — generate slots again to apply the new values.', 'vefmultibooking' ); } elseif ( $vefmb_more_starts ) { /* translators: %d: number of rules created */ $vefmb_notice = sprintf( _n( '%d rule created.', '%d rules created.', count( $vefmb_more_starts ) + 1, 'vefmultibooking' ), count( $vefmb_more_starts ) + 1 ); } else { $vefmb_notice = __( 'Rule created.', 'vefmultibooking' ); } if ( ! is_wp_error( $vefmb_result ) ) { $vefmb_notice .= VEFMB_Admin_UI::mode_note( $vefmb_avail_service->align_resource_mode( $vefmb_resource_id, '' !== $vefmb_rule_input['kind'] ? $vefmb_rule_input['kind'] : ( $vefmb_rule_id ? ( $vefmb_avail_service->get_rule( $vefmb_rule_id )['kind'] ?? 'day' ) : 'day' ) ) ); } break; case 'duplicate_rule': $vefmb_result = $vefmb_avail_service->duplicate_rule( isset( $_POST['rule_id'] ) ? absint( $_POST['rule_id'] ) : 0 ); if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } else { $vefmb_notice = __( 'Rule duplicated. The copy is inactive until you activate it.', 'vefmultibooking' ); } break; case 'update_slot_capacity': $vefmb_result = $vefmb_avail_service->update_slot( isset( $_POST['slot_id'] ) ? absint( $_POST['slot_id'] ) : 0, array( 'capacity_total' => isset( $_POST['capacity_total'] ) ? absint( $_POST['capacity_total'] ) : 0 ) ); if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } else { $vefmb_notice = __( 'Slot capacity updated.', 'vefmultibooking' ); } break; case 'save_period': $vefmb_period_id = isset( $_POST['period_id'] ) ? absint( $_POST['period_id'] ) : 0; $vefmb_period_input = array( 'name' => isset( $_POST['period_name'] ) ? sanitize_text_field( wp_unslash( $_POST['period_name'] ) ) : '', 'start_date' => isset( $_POST['period_start'] ) ? sanitize_text_field( wp_unslash( $_POST['period_start'] ) ) : '', 'end_date' => isset( $_POST['period_end'] ) ? sanitize_text_field( wp_unslash( $_POST['period_end'] ) ) : '', 'repeat_yearly' => ! empty( $_POST['period_repeat_yearly'] ), ); $vefmb_result = $vefmb_period_id ? $vefmb_periods_service->update_period( $vefmb_period_id, $vefmb_period_input ) : $vefmb_periods_service->create_period( $vefmb_resource_id, $vefmb_period_input ); if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } else { $vefmb_notice = $vefmb_period_id ? __( 'Period updated. Slots already generated are unchanged.', 'vefmultibooking' ) : __( 'Period created.', 'vefmultibooking' ); } break; case 'duplicate_period': $vefmb_result = $vefmb_periods_service->duplicate_period( isset( $_POST['period_id'] ) ? absint( $_POST['period_id'] ) : 0 ); if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } else { $vefmb_notice = __( 'Period duplicated. The copy is inactive until you activate it.', 'vefmultibooking' ); } break; case 'toggle_period_status': $vefmb_periods_service->set_status( isset( $_POST['period_id'] ) ? absint( $_POST['period_id'] ) : 0, isset( $_POST['new_status'] ) ? sanitize_key( wp_unslash( $_POST['new_status'] ) ) : '' ); $vefmb_notice = __( 'Period status updated.', 'vefmultibooking' ); break; case 'delete_period': $vefmb_periods_service->delete_period( isset( $_POST['period_id'] ) ? absint( $_POST['period_id'] ) : 0 ); $vefmb_notice = __( 'Period deleted.', 'vefmultibooking' ); break; case 'toggle_rule_status': $vefmb_avail_service->set_rule_status( isset( $_POST['rule_id'] ) ? absint( $_POST['rule_id'] ) : 0, isset( $_POST['new_status'] ) ? sanitize_key( wp_unslash( $_POST['new_status'] ) ) : '' ); $vefmb_notice = __( 'Rule status updated.', 'vefmultibooking' ); break; case 'delete_rule': $vefmb_avail_service->delete_rule( isset( $_POST['rule_id'] ) ? absint( $_POST['rule_id'] ) : 0 ); $vefmb_notice = __( 'Rule deleted. Slots already generated remain in place.', 'vefmultibooking' ); break; case 'save_slots_ahead': VEFMB_Settings::update( array( 'slots_ahead_months' => max( 1, min( 36, isset( $_POST['slots_ahead_months'] ) ? absint( $_POST['slots_ahead_months'] ) : 12 ) ) ) ); $vefmb_created = $vefmb_avail_service->generate_all_ahead(); /* translators: %d: number of slots created */ $vefmb_notice = sprintf( _n( 'Saved. %d slot created.', 'Saved. %d slots created.', $vefmb_created, 'vefmultibooking' ), $vefmb_created ); break; case 'generate_slots': $vefmb_rule_id = isset( $_POST['rule_id'] ) ? absint( $_POST['rule_id'] ) : 0; $vefmb_until = isset( $_POST['until_date'] ) ? sanitize_text_field( wp_unslash( $_POST['until_date'] ) ) : ''; $vefmb_result = $vefmb_avail_service->generate_slots_from_rule( $vefmb_rule_id, $vefmb_until ); if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } else { /* translators: %d: number of slots created */ /* translators: %d: number of slots */ $vefmb_notice = sprintf( _n( '%d slot generated.', '%d slots generated.', $vefmb_result, 'vefmultibooking' ), $vefmb_result ); } break; case 'save_slot': $vefmb_result = $vefmb_avail_service->create_slot( $vefmb_resource_id, array( 'slot_date' => isset( $_POST['slot_date'] ) ? sanitize_text_field( wp_unslash( $_POST['slot_date'] ) ) : '', 'start_time' => isset( $_POST['start_time'] ) ? sanitize_text_field( wp_unslash( $_POST['start_time'] ) ) : '', 'end_time' => isset( $_POST['end_time'] ) ? sanitize_text_field( wp_unslash( $_POST['end_time'] ) ) : '', 'capacity_total' => isset( $_POST['capacity_total'] ) ? absint( $_POST['capacity_total'] ) : 0, 'kind' => isset( $_POST['kind'] ) ? sanitize_key( wp_unslash( $_POST['kind'] ) ) : '', ) ); if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } else { $vefmb_notice = __( 'Slot created.', 'vefmultibooking' ); $vefmb_notice .= VEFMB_Admin_UI::mode_note( $vefmb_avail_service->align_resource_mode( $vefmb_resource_id, isset( $_POST['kind'] ) && 'night' === sanitize_key( wp_unslash( $_POST['kind'] ) ) ? 'night' : 'day' ) ); } break; case 'confirm_activity': $vefmb_participants_service = new VEFMB_Participants_Service(); $vefmb_confirm_slot_id = isset( $_POST['slot_id'] ) ? absint( $_POST['slot_id'] ) : 0; if ( $vefmb_participants_service->confirm_slot( $vefmb_confirm_slot_id ) ) { $vefmb_notice = __( 'Activity confirmed. The customers who had not paid yet received a payment link.', 'vefmultibooking' ); } else { $vefmb_notice = __( 'This activity could not be confirmed.', 'vefmultibooking' ); $vefmb_notice_type = 'error'; } break; case 'cancel_activity': $vefmb_cancelled_count = ( new VEFMB_Participants_Service() )->cancel_slot( isset( $_POST['slot_id'] ) ? absint( $_POST['slot_id'] ) : 0 ); /* translators: %d: number of bookings cancelled */ $vefmb_notice = sprintf( _n( 'Activity cancelled. %d booking was cancelled and its customer told by email.', 'Activity cancelled. %d bookings were cancelled and their customers told by email.', $vefmb_cancelled_count, 'vefmultibooking' ), $vefmb_cancelled_count ); break; case 'toggle_slot_status': $vefmb_avail_service->set_slot_status( isset( $_POST['slot_id'] ) ? absint( $_POST['slot_id'] ) : 0, isset( $_POST['new_status'] ) ? sanitize_key( wp_unslash( $_POST['new_status'] ) ) : '' ); $vefmb_notice = __( 'Slot status updated.', 'vefmultibooking' ); break; case 'delete_slot': $vefmb_avail_service->delete_slot( isset( $_POST['slot_id'] ) ? absint( $_POST['slot_id'] ) : 0 ); $vefmb_notice = __( 'Slot deleted.', 'vefmultibooking' ); break; case 'block_period': $vefmb_result = $vefmb_avail_service->block_period( $vefmb_resource_id, isset( $_POST['block_from'] ) ? sanitize_text_field( wp_unslash( $_POST['block_from'] ) ) : '', isset( $_POST['block_to'] ) ? sanitize_text_field( wp_unslash( $_POST['block_to'] ) ) : '', isset( $_POST['block_reason'] ) ? sanitize_text_field( wp_unslash( $_POST['block_reason'] ) ) : '', ! empty( $_POST['unit_id'] ) ? absint( $_POST['unit_id'] ) : null, isset( $_POST['block_time_from'] ) ? sanitize_text_field( wp_unslash( $_POST['block_time_from'] ) ) : '', isset( $_POST['block_time_to'] ) ? sanitize_text_field( wp_unslash( $_POST['block_time_to'] ) ) : '' ); if ( is_wp_error( $vefmb_result ) ) { $vefmb_notice = $vefmb_result->get_error_message(); $vefmb_notice_type = 'error'; } else { /* translators: %d: number of days blocked */ /* translators: %d: number of days */ $vefmb_notice = sprintf( _n( '%d day blocked.', '%d days blocked.', $vefmb_result, 'vefmultibooking' ), $vefmb_result ); } break; case 'bulk_slots': $vefmb_bulk_action = isset( $_POST['bulk_action'] ) ? sanitize_key( wp_unslash( $_POST['bulk_action'] ) ) : ''; $vefmb_bulk_ids = isset( $_POST['slot_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['slot_ids'] ) ) : array(); if ( ! in_array( $vefmb_bulk_action, array( 'close', 'open', 'delete', 'capacity' ), true ) || empty( $vefmb_bulk_ids ) ) { $vefmb_notice = __( 'Tick the slots to change, then choose an action.', 'vefmultibooking' ); $vefmb_notice_type = 'error'; break; } $vefmb_bulk = $vefmb_avail_service->bulk_slots( $vefmb_resource_id, $vefmb_bulk_ids, $vefmb_bulk_action, isset( $_POST['bulk_capacity'] ) ? absint( $_POST['bulk_capacity'] ) : 0 ); /* translators: %d: number of slots changed */ $vefmb_notice = sprintf( _n( '%d slot changed.', '%d slots changed.', $vefmb_bulk['done'], 'vefmultibooking' ), $vefmb_bulk['done'] ); if ( $vefmb_bulk['skipped'] > 0 ) { /* translators: %d: number of slots left unchanged */ $vefmb_notice .= ' ' . sprintf( _n( '%d slot was left as it was (it has bookings, or the change is not possible).', '%d slots were left as they were (they have bookings, or the change is not possible).', $vefmb_bulk['skipped'], 'vefmultibooking' ), $vefmb_bulk['skipped'] ); $vefmb_notice_type = 'warning'; } break; case 'remove_schedules': $vefmb_removed = $vefmb_avail_service->remove_rules( $vefmb_resource_id, isset( $_POST['rule_ids'] ) ? array_map( 'absint', explode( ',', sanitize_text_field( wp_unslash( $_POST['rule_ids'] ) ) ) ) : array() ); /* translators: %d: number of opening times removed */ $vefmb_notice = sprintf( _n( '%d opening time removed.', '%d opening times removed.', $vefmb_removed, 'vefmultibooking' ), $vefmb_removed ); break; case 'remove_closures': $vefmb_removed = 0; foreach ( isset( $_POST['exception_ids'] ) ? array_map( 'absint', explode( ',', sanitize_text_field( wp_unslash( $_POST['exception_ids'] ) ) ) ) : array() as $vefmb_exc_id ) { $vefmb_exc = ( new VEFMB_Availability_Exceptions_Repository() )->get( $vefmb_exc_id ); if ( $vefmb_exc && (int) $vefmb_exc['resource_id'] === $vefmb_resource_id && $vefmb_avail_service->delete_exception( $vefmb_exc_id ) ) { ++$vefmb_removed; } } /* translators: %d: number of days reopened */ $vefmb_notice = sprintf( _n( 'Closure removed (%d day).', 'Closure removed (%d days).', $vefmb_removed, 'vefmultibooking' ), $vefmb_removed ); break; case 'delete_exception': $vefmb_avail_service->delete_exception( isset( $_POST['exception_id'] ) ? absint( $_POST['exception_id'] ) : 0 ); $vefmb_notice = __( 'Exception deleted.', 'vefmultibooking' ); break; } } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only, only selects the view being displayed. $vefmb_view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : 'picker'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only. $vefmb_resource_id_get = isset( $_GET['resource_id'] ) ? absint( $_GET['resource_id'] ) : 0; ?>