PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.10
Timetics – Appointment Booking Calendar & Scheduling v1.0.10
1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 All 62 releases
timetics / core / appointments / api-appointment.php

api-appointment.php in Timetics – Appointment Booking Calendar & Scheduling 1.0.10, at core/appointments/api-appointment.php

733 lines 25.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Apointment api
4 *
5 * @since 1.0.0
6 *
7 * @package Timetics
8 */
9 namespace Timetics\Core\Appointments;
10
11 use Timetics\Base\Api;
12 use Timetics\Core\Appointments\Appointment;
13 use Timetics\Utils\Singleton;
14 use WP_Error;
15 use WP_HTTP_Response;
16 use WP_REST_Request;
17
18 /**
19 * Api_Appointment class
20 *
21 * @since 1.0.0
22 */
23 class Api_Appointment extends Api {
24
25 use Singleton;
26
27 /**
28 * Store api namespace
29 *
30 * @since 1.0.0
31 *
32 * @var string $namespace
33 */
34 protected $namespace = 'timetics/v1';
35
36 /**
37 * Store rest base
38 *
39 * @since 1.0.0
40 *
41 * @var string $rest_base
42 */
43 protected $rest_base = 'appointments';
44
45 /**
46 * Register rest routes.
47 *
48 * @since 1.0.0
49 *
50 * @return void
51 */
52 public function register_routes() {
53 /*
54 * Register route
55 */
56 register_rest_route( $this->namespace, $this->rest_base, [
57 [
58 'methods' => \WP_REST_Server::READABLE,
59 'callback' => [$this, 'get_items'],
60 'permission_callback' => function () {
61 return true;
62 },
63 ],
64 [
65 'methods' => \WP_REST_Server::CREATABLE,
66 'callback' => [$this, 'create_item'],
67 'permission_callback' => function () {
68 return current_user_can( 'read_meeting' );
69 },
70 ],
71 [
72 'methods' => \WP_REST_Server::DELETABLE,
73 'callback' => [$this, 'bulk_delete'],
74 'permission_callback' => function () {
75 return current_user_can( 'read_meeting' );
76 },
77 ],
78 ] );
79
80 /**
81 * Register route
82 *
83 * @var void
84 */
85 register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<appointment_id>[\d]+)', [
86 [
87 'methods' => \WP_REST_Server::READABLE,
88 'callback' => [$this, 'get_item'],
89 'permission_callback' => function () {
90 return true;
91 },
92 ],
93 [
94 'methods' => \WP_REST_Server::EDITABLE,
95 'callback' => [$this, 'update_item'],
96 'permission_callback' => function () {
97 return current_user_can( 'read_meeting' );
98 },
99 ],
100 [
101 'methods' => \WP_REST_Server::DELETABLE,
102 'callback' => [$this, 'delete_item'],
103 'permission_callback' => function () {
104 return current_user_can( 'read_meeting' );
105 },
106 ],
107 ] );
108
109 register_rest_route( $this->namespace, $this->rest_base . '/search', [
110 [
111 'methods' => \WP_REST_Server::READABLE,
112 'callback' => [$this, 'search_items'],
113 'permission_callback' => function () {
114 return current_user_can( 'edit_posts' );
115 },
116 ],
117 ] );
118
119 register_rest_route( $this->namespace, $this->rest_base . '/filter', [
120 [
121 'methods' => \WP_REST_Server::READABLE,
122 'callback' => [$this, 'filter_items'],
123 'permission_callback' => function () {
124 return true;
125 },
126 ],
127 ] );
128
129 register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<appointment_id>[\d]+)' . '/duplicate', [
130 [
131 'methods' => \WP_REST_Server::CREATABLE,
132 'callback' => [$this, 'duplicate_item'],
133 'permission_callback' => function () {
134 return current_user_can( 'edit_meeting' );
135 },
136 ],
137 ] );
138 }
139
140 /**
141 * Get all appointments
142 *
143 * @param WP_Rest_Request $request
144 *
145 * @return JSON
146 */
147 public function get_items( $request ) {
148
149 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
150 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
151 $type = ! empty( $request['type'] ) ? sanitize_text_field( $request['type'] ) : '';
152
153 $args = [
154 'posts_per_page' => $per_page,
155 'paged' => $paged,
156 'type' => $type,
157 ];
158
159 if ( ! current_user_can( 'edit_meeting' ) ) {
160 $args['staff'] = get_current_user_id();
161 }
162
163 $appoint = Appointment::all( $args );
164
165 $items = [];
166
167 foreach ( $appoint['items'] as $item ) {
168 $items[] = $this->prepare_item( $item->ID );
169 }
170
171 $data = [
172 'success' => 1,
173 'status_code' => 200,
174 'data' => [
175 'total' => $appoint['total'],
176 'items' => $items,
177 ],
178 ];
179
180 return rest_ensure_response( $data );
181 }
182
183 /**
184 * Search appointment
185 *
186 * @param Object $request
187 *
188 * @return JSON
189 */
190 public function search_items( $request ) {
191 // Prepare search args.
192 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
193 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
194 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
195
196 // Get search.
197 $appointments = new \WP_Query(
198 array(
199 'post_type' => 'timetics-appointment',
200 'posts_per_page' => $per_page,
201 'paged' => $paged,
202 'orderby' => 'ID',
203 'order' => 'DESC',
204
205 // @codingStandardsIgnoreStart
206 'meta_query' => array(
207 'relation' => 'OR',
208 array(
209 'key' => '_tt_apointment_name',
210 'value' => $search,
211 'compare' => 'LIKE',
212 ),
213 array(
214 'key' => '_tt_apointment_type',
215 'value' => $search,
216 'compare' => 'LIKE',
217 ),
218 array(
219 'key' => '_tt_apointment_description',
220 'value' => $search,
221 'compare' => 'LIKE',
222 ),
223 array(
224 'key' => '_tt_apointment_location',
225 'value' => $search,
226 'compare' => 'LIKE',
227 ),
228 array(
229 'key' => '_tt_apointment_duration',
230 'value' => $search,
231 'compare' => 'LIKE',
232 ),
233 array(
234 'key' => '_tt_apointment_schedule',
235 'value' => $search,
236 'compare' => 'LIKE',
237 ),
238 ),
239 // @codingStandardsIgnoreEnd
240 )
241 );
242
243 // Prepare items for response.
244 $items = [];
245
246 foreach ( $appointments->posts as $item ) {
247 $items[] = $this->prepare_item( $item->ID );
248 }
249
250 $data = [
251 'success' => 1,
252 'status' => 200,
253 'data' => [
254 'total' => $appointments->found_posts,
255 'items' => $items,
256 ],
257 ];
258
259 return rest_ensure_response( $data );
260 }
261
262 public function filter_items( $request ) {
263 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
264 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
265 $staff = ! empty( $request['staff_id'] ) ? intval( $request['staff_id'] ) : 0;
266 $category = ! empty( $request['category'] ) ? intval( $request['category'] ) : 0;
267 $visibility = ! empty( $request['visibility'] ) ? sanitize_text_field( $request['visibility'] ) : 'enabled';
268
269 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
270 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
271
272 $appoint = Appointment::all( [
273 'posts_per_page' => $per_page,
274 'paged' => $paged,
275 'visibility' => $visibility,
276 'staff' => $staff,
277 'category' => $category,
278 ] );
279
280 $items = [];
281
282 foreach ( $appoint['items'] as $item ) {
283 $items[] = $this->prepare_item( $item->ID );
284 }
285
286 $data = [
287 'success' => 1,
288 'status' => 200,
289 'data' => [
290 'total' => $appoint['total'],
291 'items' => $items,
292 ],
293 ];
294
295 return rest_ensure_response( $data );
296 }
297
298 /**
299 * Create appointment
300 *
301 * @param WP_Rest_Request $request
302 *
303 * @return JSON Newly created appointment data
304 */
305 public function create_item( $request ) {
306 /**
307 * Added temporary for leagacy sass. It will remove in future.
308 */
309 $args['staff'] = get_current_user_id();
310 $meetings_count = Appointment::all( $args );
311 $data = json_decode( $request->get_body(), true );
312
313 $response = [
314 'success' => 0,
315 'status_code' => 502,
316 'message' => esc_html__( 'Something went wrong', 'timetics' ),
317 'data' => [],
318 ];
319
320 if ( ! empty( $data['price'] ) && apply_filters( 'timetics/staff/appointment/price_check', false, $data['price'] ) == true ) {
321 return rest_ensure_response( apply_filter( 'timetics/admin/appointment/error_data', $response, 'price_check' ) );
322 }
323
324 if ( apply_filters( 'timetics/staff/appointment/count_check', false, $meetings_count ) == true ) {
325 return rest_ensure_response( apply_filter( 'timetics/admin/appointment/error_data', $response, 'count_check' ) );
326 }
327
328 $type = ! empty( $data['type'] ) ? sanitize_text_field( $data['type'] ) : '';
329
330 if ( apply_filters( 'timetics/staff/appointment/type_check', false, $type ) == true ) {
331 return rest_ensure_response( apply_filter( 'timetics/admin/appointment/error_data', $response, 'type_check' ) );
332 } // End
333
334
335 return $this->save_appointment( $request );
336 }
337
338 /**
339 * Update appointment
340 *
341 * @param WP_Rest_Request $request
342 *
343 * @return JSON Updated appointment data
344 */
345 public function update_item( $request ) {
346 $appointment_id = (int) $request['appointment_id'];
347 $appoint = new Appointment( $appointment_id );
348
349 $data = json_decode( $request->get_body(), true );
350
351 /**
352 * Added temporary for leagacy sass. It will remove in future.
353 */
354 $response = [
355 'success' => 0,
356 'status_code' => 502,
357 'message' => esc_html__( 'Something went wrong', 'timetics' ),
358 'data' => [],
359 ];
360
361 if ( ! empty( $data['price'] ) && apply_filters( 'timetics/staff/appointment/price_check', false, $data['price'] ) == true ) {
362 return rest_ensure_response( apply_filter( 'timetics/admin/appointment/error_data', $response, 'price_check' ) );
363 } // End.
364
365 if ( ! $appoint->is_appointment() ) {
366
367 $response = [
368 'success' => 0,
369 'status_code' => 404,
370 'message' => esc_html__( 'Invalid appointment id.', 'timetics' ),
371 'data' => [],
372 ];
373
374 return new WP_HTTP_Response( $response, 404 );
375 }
376
377 return $this->save_appointment( $request, $appointment_id );
378 }
379
380 /**
381 * Get single appointment
382 *
383 * @param WP_Rest_Requesr $request
384 *
385 * @return JSON Single appointment data
386 */
387 public function get_item( $request ) {
388 $appoinment_id = (int) $request['appointment_id'];
389 $appoint = new Appointment( $appoinment_id );
390
391 if ( ! $appoint->is_appointment() ) {
392
393 $data = [
394 'status_code' => 404,
395 'message' => esc_html__( 'Invalid appointment id.', 'timetics' ),
396 'data' => [],
397 ];
398
399 return new WP_HTTP_Response( $data, 404 );
400 }
401
402 $response = [
403 'status_code' => 200,
404 'message' => esc_html__( 'Successfully retrieved appointments', 'timetics' ),
405 'data' => $this->prepare_item( $appoint ),
406 ];
407
408 return rest_ensure_response( $response );
409 }
410
411 /**
412 * Delete single appointment
413 *
414 * @param WP_Rest_Request $request
415 *
416 * @return
417 */
418 public function delete_item( $request ) {
419 $appoinment_id = (int) $request['appointment_id'];
420 $appoint = new Appointment( $appoinment_id );
421
422 if ( ! $appoint->is_appointment() ) {
423 return [
424 'status_code' => 404,
425 'message' => esc_html__( 'Invalid appointment id.', 'timetics' ),
426 'data' => [],
427 ];
428 }
429
430 $appoint->delete();
431
432 $response = [
433 'status_code' => 201,
434 'message' => esc_html__( 'Successfully deleted appointment', 'timetics' ),
435 'data' => [
436 'item' => $appoinment_id,
437 ],
438 ];
439
440 return rest_ensure_response( $response );
441 }
442
443 /**
444 * Delete multiples
445 *
446 * @param WP_Rest_Request $request
447 *
448 * @return JSON
449 */
450 public function bulk_delete( $request ) {
451 $appointments = json_decode( $request->get_body(), true );
452
453 foreach ( $appointments as $appoint ) {
454 $appoint = new Appointment( $appoint );
455
456 if ( ! $appoint->is_appointment() ) {
457 $data = [
458 'success' => 0,
459 'status' => 404,
460 'message' => esc_html__( 'Invalid appointment id.', 'timetics' ),
461 'data' => [],
462 ];
463
464 return new WP_HTTP_Response( $data, 404 );
465 }
466
467 $appoint->delete();
468 }
469
470 return rest_ensure_response( [
471 'success' => 1,
472 'status' => 201,
473 'message' => esc_html__( 'Successfully deleted all appointments', 'timetics' ),
474 'data' => [
475 'items' => $appointments,
476 ],
477 ] );
478 }
479
480 /**
481 * Duplicate appointment
482 *
483 * @since 1.0.0
484 *
485 * @param object $request
486 *
487 * @return JSON
488 */
489 public function duplicate_item( $request ) {
490 /**
491 * Added temporary for leagacy sass. It will remove in future.
492 */
493
494 $args['staff'] = get_current_user_id();
495 $meetings_count = Appointment::all( $args );
496 $data = json_decode( $request->get_body(), true );
497
498 $response = [
499 'success' => 0,
500 'status_code' => 502,
501 'message' => esc_html__( 'Something went wrong', 'timetics' ),
502 'data' => [],
503 ];
504
505 if ( ! empty( $data['price'] ) && apply_filters( 'timetics/staff/appointment/price_check', false, $data['price'] ) == true ) {
506 return rest_ensure_response( apply_filter( 'timetics/admin/appointment/error_data', $response, 'price_check' ) );
507 }
508
509 if ( apply_filters( 'timetics/staff/appointment/count_check', false, $meetings_count ) == true ) {
510 return rest_ensure_response( apply_filter( 'timetics/admin/appointment/error_data', $response, 'count_check' ) );
511 }
512
513 $type = ! empty( $data['type'] ) ? sanitize_text_field( $data['type'] ) : '';
514
515 if ( apply_filters( 'timetics/staff/appointment/type_check', false, $type ) == true ) {
516 return rest_ensure_response( apply_filter( 'timetics/admin/appointment/error_data', $response, 'type_check' ) );
517 } // End.
518
519 $appoinment_id = (int) $request['appointment_id'];
520 $appoint = new Appointment( $appoinment_id );
521
522 if ( ! $appoint->is_appointment() ) {
523 return new WP_HTTP_Response(
524 [
525 'success' => 0,
526 'status_code' => 404,
527 'message' => esc_html__( 'Invalid appointment id.', 'timetics' ),
528 'data' => [],
529 ],
530 404
531 );
532 }
533
534 $appoint->duplicate();
535
536 $item = $this->prepare_item( $appoint );
537
538 $response = [
539 'success' => 1,
540 'status_code' => 201,
541 'message' => esc_html__( 'Successfully duplicated appointment', 'timetics' ),
542 'data' => $item,
543 ];
544
545 return rest_ensure_response( $response );
546 }
547
548 /**
549 * Save appointment
550 *
551 * @param WP_Rest_Request $request
552 * @param integer $id Appointment id
553 *
554 * @return JSON Updated appoitment data
555 */
556 public function save_appointment( $request, $id = 0 ) {
557 $appoint = new Appointment( $id );
558
559 $data = json_decode( $request->get_body(), true );
560
561 $data = apply_filters( 'timetics_meeting_data', $data );
562
563 $name = ! empty( $data['name'] ) ? sanitize_text_field( $data['name'] ) : $appoint->get_name();
564 $type = ! empty( $data['type'] ) ? sanitize_text_field( $data['type'] ) : $appoint->get_type();
565 $description = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : '';
566 $staff = ! empty( $data['staff'] ) ? array_map( 'intval', $data['staff'] ) : $appoint->get_staff();
567 $locations = ! empty( $data['locations'] ) ? $data['locations'] : $appoint->get_locations();
568 $duration = ! empty( $data['duration'] ) ? sanitize_text_field( $data['duration'] ) : '';
569 $schedule = ! empty( $data['schedule'] ) ? $data['schedule'] : $appoint->get_schedule();
570 $blocked_schedule = ! empty( $data['blocked_schedule'] ) ? $data['blocked_schedule'] : $appoint->get_blocked_schedule();
571 $price = ! empty( $data['price'] ) ? $data['price'] : '';
572 $categories = ! empty( $data['categories'] ) ? $data['categories'] : '';
573 $buffer_time = ! empty( $data['buffer_time'] ) ? $data['buffer_time'] : '';
574 $timezone = ! empty( $data['timezone'] ) ? $data['timezone'] : '';
575 $availability = ! empty( $data['availability'] ) ? $data['availability'] : '';
576 $visibility = ! empty( $data['visibility'] ) ? strtolower( $data['visibility'] ) : 'enabled';
577 $notifications = ! empty( $data['notifications'] ) ? $data['notifications'] : '';
578 $fleunt_crm_webhook = ! empty( $data['fleunt_crm_webhook'] ) ? $data['fleunt_crm_webhook'] : '';
579 $fluent_hook_overwrite = ! empty( $data['fluent_hook_overwrite'] ) ? (bool) $data['fluent_hook_overwrite'] : false;
580 $pabbly_hook_overwrite = ! empty( $data['pabbly_hook_overwrite'] ) ? (bool) $data['pabbly_hook_overwrite'] : false;
581 $zapier_hook_overwrite = ! empty( $data['zapier_hook_overwrite'] ) ? (bool) $data['zapier_hook_overwrite'] : false;
582 $pabbly_webook = ! empty( $data['pabbly_webook'] ) ? $data['pabbly_webook'] : '';
583 $zapier_webook = ! empty( $data['zapier_webook'] ) ? $data['zapier_webook'] : '';
584 $min_notice_time = ! empty( $data['min_notice_time'] ) ? $data['min_notice_time'] : '';
585 $custom_fields = ! empty( $data['custom_fields'] ) ? $data['custom_fields'] : [];
586 $capacity = ! empty( $data['capacity'] ) ? intval( $data['capacity'] ) : 1;
587 $action = $id ? 'update' : 'created';
588
589 if ( is_array( $price ) ) {
590 $ticket_quantity = 0;
591
592 foreach( $price as $ticket ) {
593 if ( ! empty( $ticket['ticket_quantity'] ) ) {
594 $ticket_quantity += intval( $ticket['ticket_quantity'] );
595 }
596 }
597
598 $capacity = $ticket_quantity;
599 }
600
601 $validate_data = [
602 'name' => $name,
603 'type' => $type,
604 'locations' => $locations,
605 'schedule' => $schedule,
606 'blocked_schedule' => $blocked_schedule,
607 'staff' => $staff,
608
609 ];
610 // Validate input data.
611 $validate = $this->validate( $validate_data, [
612 'name',
613 'type',
614 'locations',
615 'schedule',
616 'staff',
617 ] );
618
619 if ( ! timetics_is_valid_timezone( $timezone ) ) {
620 return new WP_Error( 'timezone_error', __( 'Your timezone is invaid.', 'timetics' ) );
621 }
622
623 if ( is_wp_error( $validate ) ) {
624 $data = [
625 'status_code' => 409,
626 'success' => 0,
627 'message' => $validate->get_error_messages(),
628 'data' => [],
629 ];
630
631 return new WP_HTTP_Response( $data, 409 );
632 }
633
634 // Save appointment.
635 $appointment_data = [
636 'name' => $name,
637 'description' => $description,
638 'type' => $type,
639 'locations' => $locations,
640 'staff' => $staff,
641 'duration' => $duration,
642 'price' => $price,
643 'capacity' => $capacity,
644 'schedule' => $schedule,
645 'blocked_schedule' => $blocked_schedule,
646 'categories' => $categories,
647 'timezone' => $timezone,
648 'availability' => $availability,
649 'visibility' => $visibility,
650 'buffer_time' => $buffer_time,
651 'notifications' => $notifications,
652 'fleunt_crm_webhook' => $fleunt_crm_webhook,
653 'fluent_hook_overwrite' => $fluent_hook_overwrite,
654 'pabbly_hook_overwrite' => $pabbly_hook_overwrite,
655 'zapier_hook_overwrite' => $zapier_hook_overwrite,
656 'pabbly_webook' => $pabbly_webook,
657 'zapier_webook' => $zapier_webook,
658 'min_notice_time' => $min_notice_time,
659 'custom_fields' => $custom_fields,
660
661 ];
662
663 $appointment_data = apply_filters( 'timetics_meeting_insert_data', $data, $appointment_data );
664
665 $appoint->set_props( $appointment_data );
666 $appoint->save();
667
668 // Assign meeting category.
669 wp_set_post_terms( $appoint->get_id(), $categories, 'timetics-meeting-category' );
670
671 do_action( 'timetics_meeting_after_insert', $appoint, $data );
672
673 // Prepare response data.
674 $item = $this->prepare_item( $appoint );
675
676 $response = [
677 'status_code' => 201,
678 'success' => 1,
679 'message' => sprintf( esc_html__( 'Succssfully %s appointment', 'timetics' ), $action ),
680 'data' => $item,
681 ];
682
683 return rest_ensure_response( $response );
684 }
685
686 /**
687 * Prepare item for response
688 *
689 * @param integer $appoinment_id
690 *
691 * @return array
692 */
693 public function prepare_item( $appoint_id, $timezone = '' ) {
694 $appointment = new Appointment( $appoint_id );
695 $dulicate = $appointment->get_duplicate_nuber();
696 $dulicate_text = $dulicate ? ' -Duplicate' : '';
697 $custom_fields = $appointment->get_custom_fields();
698 $data = [
699 'id' => $appointment->get_id(),
700 'name' => $appointment->get_name() . $dulicate_text,
701 'image' => $appointment->get_image(),
702 // 'link' => $appointment->get_link(),
703 'description' => $appointment->get_description(),
704 'type' => $appointment->get_type(),
705 'locations' => $appointment->get_locations(),
706 'schedule' => $appointment->get_schedule(),
707 'blocked_schedule' => $appointment->get_blocked_schedule(),
708 'price' => $appointment->get_price(),
709 'categories' => $appointment->get_category_ids(),
710 'staff' => $appointment->get_staff(),
711 'buffer_time' => $appointment->get_buffer_time(),
712 'timezone' => $appointment->get_timezone(),
713 'availability' => $appointment->get_availability(),
714 'visibility' => $appointment->get_visibility(),
715 'duration' => $appointment->get_duration(),
716 'notifications' => $appointment->get_notifications(),
717 'capacity' => $appointment->get_capacity(),
718 'fluent_hook_overwrite' => $appointment->get_fluent_hook_overwrite(),
719 'fleunt_crm_webhook' => $appointment->get_fleunt_crm_webhook(),
720 'pabbly_hook_overwrite' => $appointment->get_pabbly_hook_overwrite(),
721 'pabbly_webook' => $appointment->get_pabbly_webook(),
722 'zapier_hook_overwrite' => $appointment->get_zapier_hook_overwrite(),
723 'zapier_webook' => $appointment->get_zapier_webook(),
724 'min_notice_time' => $appointment->get_min_notice_time(),
725 'custom_fields' => $custom_fields ?: [],
726 'permalink' => get_permalink( $appointment->get_id() ),
727 ];
728
729 return apply_filters( 'timetics_meeting_json_data', $data, $appointment );
730 }
731
732 }
733