PluginProbe
Booking Calendar / 11.6.1
Booking Calendar v11.6.1
11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 All 204 releases
booking / includes / page-availability-timeslots / ajax / availability_timeslots__save.php

availability_timeslots__save.php in Booking Calendar 11.6.1, at includes/page-availability-timeslots/ajax/availability_timeslots__save.php

310 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AJAX save endpoint for time slots availability.
4 *
5 * @package Booking Calendar
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Parse posted intervals.
14 *
15 * @return array
16 */
17 function wpbc_availability_timeslots__get_posted_intervals() {
18
19 $posted_intervals = isset( $_POST['intervals'] ) ? wp_unslash( $_POST['intervals'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
20
21 if ( is_string( $posted_intervals ) ) {
22 $decoded = json_decode( $posted_intervals, true );
23 if ( is_array( $decoded ) ) {
24 $posted_intervals = $decoded;
25 }
26 }
27
28 if ( ! is_array( $posted_intervals ) ) {
29 return array();
30 }
31
32 $intervals = array();
33
34 foreach ( $posted_intervals as $interval ) {
35 if ( ! is_array( $interval ) || empty( $interval['date'] ) ) {
36 continue;
37 }
38
39 if ( isset( $interval['start_second'], $interval['end_second'] ) ) {
40 $start_second = $interval['start_second'];
41 $end_second = $interval['end_second'];
42 } else {
43 $start_second = isset( $interval['start'] ) ? intval( $interval['start'] ) * MINUTE_IN_SECONDS : 0;
44 $end_second = isset( $interval['end'] ) ? intval( $interval['end'] ) * MINUTE_IN_SECONDS : 0;
45 }
46
47 $normalized_interval = wpbc_availability_timeslots__normalize_interval( $interval['date'], $start_second, $end_second );
48 if ( null !== $normalized_interval ) {
49 $intervals[] = $normalized_interval;
50 }
51 }
52
53 return $intervals;
54 }
55
56 /**
57 * Group intervals by date.
58 *
59 * @param array $intervals Intervals.
60 *
61 * @return array
62 */
63 function wpbc_availability_timeslots__group_intervals_by_date( $intervals ) {
64
65 $grouped = array();
66
67 foreach ( $intervals as $interval ) {
68 if ( ! isset( $grouped[ $interval['date'] ] ) ) {
69 $grouped[ $interval['date'] ] = array();
70 }
71 $grouped[ $interval['date'] ][] = array(
72 (int) $interval['start_second'],
73 (int) $interval['end_second'],
74 );
75 }
76
77 return $grouped;
78 }
79
80 /**
81 * Subtract one interval from a list of intervals.
82 *
83 * @param array $intervals Intervals.
84 * @param array $remove_interval Interval to remove.
85 *
86 * @return array
87 */
88 function wpbc_availability_timeslots__subtract_interval( $intervals, $remove_interval ) {
89
90 $result = array();
91 $remove_start = (int) $remove_interval[0];
92 $remove_end = (int) $remove_interval[1];
93
94 foreach ( $intervals as $interval ) {
95 $start = (int) $interval[0];
96 $end = (int) $interval[1];
97
98 if ( $remove_end <= $start || $remove_start >= $end ) {
99 $result[] = array( $start, $end );
100 continue;
101 }
102
103 if ( $start < $remove_start ) {
104 $result[] = array( $start, min( $remove_start, $end ) );
105 }
106 if ( $end > $remove_end ) {
107 $result[] = array( max( $remove_end, $start ), $end );
108 }
109 }
110
111 return wpbc_merge_intersected_intervals( $result );
112 }
113
114 /**
115 * Get existing active intervals for one date/resource.
116 *
117 * @param int $resource_id Resource ID.
118 * @param string $date Date.
119 *
120 * @return array
121 */
122 function wpbc_availability_timeslots__get_existing_intervals_for_date( $resource_id, $date ) {
123
124 $rows = wpbc_availability_timeslots__get_intervals_for_resources(
125 array(
126 'resource_id' => $resource_id,
127 'date_start' => $date,
128 'date_end' => $date,
129 'use_cache' => false,
130 )
131 );
132 $intervals = array();
133
134 if ( ! empty( $rows[ $date ][ $resource_id ] ) ) {
135 foreach ( $rows[ $date ][ $resource_id ] as $row ) {
136 $intervals[] = array(
137 (int) $row['start_second'],
138 (int) $row['end_second'],
139 );
140 }
141 }
142
143 return wpbc_merge_intersected_intervals( $intervals );
144 }
145
146 /**
147 * Replace active intervals for one resource/date.
148 *
149 * @param int $resource_id Resource ID.
150 * @param string $date Date.
151 * @param array $intervals Intervals.
152 * @param string $group_uid Group UID.
153 *
154 * @return void
155 */
156 function wpbc_availability_timeslots__replace_intervals_for_date( $resource_id, $date, $intervals, $group_uid ) {
157
158 global $wpdb;
159
160 $table = wpbc_availability_timeslots__table_name();
161 $current_user_id = wpbc_get_current_user_id();
162 $now = current_time( 'mysql' );
163 $intervals = wpbc_merge_intersected_intervals( $intervals );
164
165 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
166 $wpdb->delete(
167 $table,
168 array(
169 'resource_id' => $resource_id,
170 'block_date' => $date,
171 'status' => 'active',
172 ),
173 array( '%d', '%s', '%s' )
174 );
175
176 foreach ( $intervals as $interval ) {
177 if ( (int) $interval[0] >= (int) $interval[1] ) {
178 continue;
179 }
180
181 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
182 $wpdb->insert(
183 $table,
184 array(
185 'resource_id' => $resource_id,
186 'block_date' => $date,
187 'start_second' => (int) $interval[0],
188 'end_second' => (int) $interval[1],
189 'status' => 'active',
190 'group_uid' => $group_uid,
191 'source' => 'manual',
192 'note' => '',
193 'created_by' => $current_user_id,
194 'modified_by' => $current_user_id,
195 'creation_date' => $now,
196 'modification_date' => $now,
197 ),
198 array( '%d', '%s', '%d', '%d', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s' )
199 );
200 }
201 }
202
203 /**
204 * Delete intervals by IDs.
205 *
206 * @param array $ids IDs.
207 *
208 * @return int
209 */
210 function wpbc_availability_timeslots__delete_by_ids( $ids ) {
211
212 global $wpdb;
213
214 $ids = array_values( array_filter( array_map( 'intval', $ids ) ) );
215 if ( empty( $ids ) ) {
216 return 0;
217 }
218
219 $table = wpbc_availability_timeslots__table_name();
220 $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
221 $query_values = $ids;
222
223 $sql = $wpdb->prepare(
224 "DELETE FROM {$table}
225 WHERE timeslot_id IN ({$placeholders})",
226 $query_values
227 );
228
229 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
230 return (int) $wpdb->query( $sql );
231 }
232
233 /**
234 * AJAX: save blocked/unblocked time slots.
235 *
236 * @return void
237 */
238 function wpbc_availability_timeslots_ajax_save() {
239
240 $nonce_result = check_ajax_referer( 'wpbc_availability_timeslots_ajax_nonce', 'nonce', false );
241 if ( false === $nonce_result ) {
242 wp_send_json_error( array( 'message' => __( 'Security check failed.', 'booking' ) ), 403 );
243 }
244
245 if ( ! current_user_can( wpbc_availability_timeslots__get_manage_cap() ) ) {
246 wp_send_json_error( array( 'message' => __( 'You do not have permission to change availability.', 'booking' ) ), 403 );
247 }
248
249 $resource_id = isset( $_POST['resource_id'] ) ? intval( $_POST['resource_id'] ) : wpbc_get_default_resource(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
250 $mode = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : 'block'; // phpcs:ignore WordPress.Security.NonceVerification.Missing
251 $intervals = wpbc_availability_timeslots__get_posted_intervals();
252 $group_uid = wp_generate_uuid4();
253
254 if ( 'delete' === $mode ) {
255 $ids = isset( $_POST['ids'] ) ? wp_unslash( $_POST['ids'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
256 $ids = is_array( $ids ) ? $ids : json_decode( $ids, true );
257 $changed = wpbc_availability_timeslots__delete_by_ids( is_array( $ids ) ? $ids : array() );
258 wp_send_json_success(
259 array(
260 'changed' => $changed,
261 )
262 );
263 }
264
265 if ( empty( $intervals ) ) {
266 wp_send_json_error( array( 'message' => __( 'No time slots selected.', 'booking' ) ), 400 );
267 }
268
269 $grouped_intervals = wpbc_availability_timeslots__group_intervals_by_date( $intervals );
270 $changed_dates = array();
271
272 foreach ( $grouped_intervals as $date => $new_intervals ) {
273 $existing_intervals = wpbc_availability_timeslots__get_existing_intervals_for_date( $resource_id, $date );
274
275 if ( 'unblock' === $mode ) {
276 $next_intervals = $existing_intervals;
277 foreach ( wpbc_merge_intersected_intervals( $new_intervals ) as $remove_interval ) {
278 $next_intervals = wpbc_availability_timeslots__subtract_interval( $next_intervals, $remove_interval );
279 }
280 } else {
281 $next_intervals = wpbc_merge_intersected_intervals( array_merge( $existing_intervals, $new_intervals ) );
282 }
283
284 wpbc_availability_timeslots__replace_intervals_for_date( $resource_id, $date, $next_intervals, $group_uid );
285 $changed_dates[] = $date;
286 }
287
288 sort( $changed_dates );
289
290 $saved_intervals = wpbc_availability_timeslots__get_intervals_for_resources(
291 array(
292 'resource_id' => $resource_id,
293 'date_start' => $changed_dates[0],
294 'date_end' => $changed_dates[ count( $changed_dates ) - 1 ],
295 'use_cache' => false,
296 )
297 );
298
299 wp_send_json_success(
300 array(
301 'resource_id' => $resource_id,
302 'mode' => $mode,
303 'group_uid' => $group_uid,
304 'dates' => $changed_dates,
305 'intervals' => $saved_intervals,
306 )
307 );
308 }
309 add_action( 'wp_ajax_WPBC_AJX_AVAILABILITY_TIMESLOTS_SAVE', 'wpbc_availability_timeslots_ajax_save' );
310