PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.63
Timetics – Appointment Booking Calendar & Scheduling v1.0.63
1.0.62 1.0.63 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 All 64 releases
timetics / core / staffs / api-staff.php

api-staff.php in Timetics – Appointment Booking Calendar & Scheduling 1.0.63, at core/staffs/api-staff.php

698 lines 21.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Api Staff
4 *
5 * @package Timetics
6 */
7 namespace Timetics\Core\Staffs;
8
9 defined( 'ABSPATH' ) || exit;
10
11 use Timetics\Base\Api;
12 use Timetics\Core\Integrations\Google\Client;
13 use Timetics\Core\Staffs\Staff;
14 use Timetics\Utils\Singleton;
15 use Timetics\Core\Emails\Re_Invite_Staff;
16 use WP_Error;
17 use WP_HTTP_Response;
18 use WP_User_Query;
19
20 /**
21 * Class Api_Staff
22 */
23 class Api_Staff extends Api {
24 use Singleton;
25
26 /**
27 * Store namespace
28 *
29 * @var string
30 */
31 protected $namespace = 'timetics/v1';
32
33 /**
34 * Store rest base
35 *
36 * @var string
37 */
38 protected $rest_base = 'staffs';
39
40 /**
41 * Register rest route
42 *
43 * @return void
44 */
45 public function register_routes() {
46 register_rest_route(
47 $this->namespace, $this->rest_base, [
48 [
49 'methods' => \WP_REST_Server::READABLE,
50 'callback' => [$this, 'get_items'],
51 'permission_callback' => [$this, 'get_staffs_permission_callback'],
52 ],
53 [
54 'methods' => \WP_REST_Server::CREATABLE,
55 'callback' => [$this, 'create_item'],
56 'permission_callback' => function () {
57 return current_user_can( 'manage_timetics' );
58 },
59 ],
60 [
61 'methods' => \WP_REST_Server::DELETABLE,
62 'callback' => [$this, 'bulk_delete'],
63 'permission_callback' => function () {
64 return current_user_can( 'manage_options' );
65 },
66 ],
67 ]
68 );
69
70 /**
71 * Register route
72 *
73 * @var void
74 */
75 register_rest_route(
76 $this->namespace, '/' . $this->rest_base . '/(?P<staff_id>[\d]+)', [
77 [
78 'methods' => \WP_REST_Server::READABLE,
79 'callback' => [$this, 'get_item'],
80 'permission_callback' => function () {
81 return true;
82 },
83 ],
84 [
85 'methods' => \WP_REST_Server::EDITABLE,
86 'callback' => [$this, 'update_item'],
87 'permission_callback' => function ( $request ) {
88 return current_user_can( 'manage_options' ) || (int) $request['staff_id'] === get_current_user_id();
89 },
90 ],
91 [
92 'methods' => \WP_REST_Server::DELETABLE,
93 'callback' => [$this, 'delete_item'],
94 'permission_callback' => function () {
95 return current_user_can( 'manage_options' );
96 },
97 ],
98 [
99 'methods' => \WP_REST_Server::READABLE,
100 'callback' => [$this, 'get_item'],
101 'permission_callback' => function () {
102 return true;
103 },
104 ],
105 ]
106 );
107
108 register_rest_route(
109 $this->namespace, $this->rest_base . '/re-invite'. '/(?P<staff_id>[\d]+)', [
110 [
111 'methods' => \WP_REST_Server::READABLE,
112 'callback' => [$this, 're_invite_staff'],
113 'permission_callback' => function () {
114 return current_user_can( 'manage_options' );
115 },
116 ],
117 ]
118 );
119
120 register_rest_route(
121 $this->namespace, $this->rest_base . '/search', [
122 [
123 'methods' => \WP_REST_Server::READABLE,
124 'callback' => [$this, 'search_items'],
125 'permission_callback' => function () {
126 return current_user_can( 'manage_timetics' );
127 },
128 ],
129 ]
130 );
131
132 register_rest_route(
133 $this->namespace, $this->rest_base . '/(?P<staff_id>[\d]+)/integrations', [
134 [
135 'methods' => \WP_REST_Server::READABLE,
136 'callback' => [$this, 'get_integrations'],
137 'permission_callback' => function ( $request ) {
138 return current_user_can( 'manage_options' ) || (int) $request['staff_id'] === get_current_user_id();
139 },
140 ],
141 ]
142 );
143
144 register_rest_route(
145 $this->namespace, $this->rest_base . '/(?P<staff_id>[\d]+)/integrations/auth-revoke', [
146 [
147 'methods' => \WP_REST_Server::READABLE,
148 'callback' => [$this, 'auth_revoke'],
149 'permission_callback' => function ( $request ) {
150 return current_user_can( 'manage_options' ) || (int) $request['staff_id'] === get_current_user_id();
151 },
152 ],
153 ]
154 );
155 }
156
157 public function re_invite_staff( $request ) {
158 $id = ! empty( $request['staff_id'] ) ? intval( $request['staff_id'] ) : null ;
159 $success = true;
160 $message = esc_html__("Invitation Email send successfully","timetics");
161 if ( empty( $id ) ) {
162 $success = true;
163 $message = esc_html__("Staff ID missing","timetics");
164 }
165 $re_invite_staff = new Re_Invite_Staff( $id );
166 $re_invite_staff->send();
167
168 $data = [
169 'success' => 1,
170 'message' => $message,
171 'data' => [
172 'sent' => $success,
173 ]
174 ];
175
176 return rest_ensure_response( $data );
177 }
178
179 /**
180 * Get all appointments
181 *
182 * @param WP_Rest_Request $request
183 *
184 * @return JSON
185 */
186 public function get_items( $request ) {
187 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
188 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
189
190 $staff = Staff::all(
191 [
192 'number' => $per_page,
193 'paged' => $paged,
194 ]
195 );
196
197 $items = [];
198
199 foreach ( $staff['items'] as $item ) {
200 $items[] = $this->prepare_item( $item->ID );
201 }
202
203 /**
204 * Added temporary for leagacy sass. It will remove in future.
205 */
206 $items = apply_filters( 'timetics/admin/staff/get_items', $items );
207
208 $data = [
209 'success' => 1,
210 'data' => [
211 'total' => $staff['total'],
212 'items' => $items,
213 ],
214 ];
215
216 return rest_ensure_response( $data );
217 }
218
219 /**
220 * Get single staff
221 *
222 * @param WP_Rest_Requesr $request
223 *
224 * @return JSON Single staff data
225 */
226 public function get_item( $request ) {
227 $staff_id = (int) $request['staff_id'];
228 $staff = new Staff( $staff_id );
229
230 if ( ! $staff->is_staff() ) {
231 return [
232 'status_code' => 404,
233 'message' => esc_html__( 'Invalid staff id.', 'timetics' ),
234 'data' => [],
235 ];
236 }
237
238 $response = [
239 'status_code' => 200,
240 'data' => $this->prepare_item( $staff ),
241 ];
242
243 return rest_ensure_response( $response );
244 }
245
246 /**
247 * Search staff
248 *
249 * @return JSON
250 */
251 public function search_items( $request ) {
252
253 // Prepare search args.
254 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
255 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
256 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
257
258 // Get search.
259 $users = new WP_User_Query(
260 array(
261 'role' => 'timetics-staff',
262 'number' => $per_page,
263 'paged' => $paged,
264
265 // @codingStandardsIgnoreStart
266 'meta_query' => array(
267 'relation' => 'OR',
268 array(
269 'key' => 'first_name',
270 'value' => $search,
271 'compare' => 'LIKE',
272 ),
273 array(
274 'key' => 'last_name',
275 'value' => $search,
276 'compare' => 'LIKE',
277 ),
278 array(
279 'key' => '_staff_email',
280 'value' => $search,
281 'compare' => 'LIKE',
282 ),
283 array(
284 'key' => '_staff_user_name',
285 'value' => $search,
286 'compare' => 'LIKE',
287 ),
288 array(
289 'key' => '_staff_phone',
290 'value' => $search,
291 'compare' => 'LIKE',
292 ),
293 ),
294 // @codingStandardsIgnoreEnd
295 )
296 );
297
298 // Prepare items for response.
299 $items = [];
300
301 foreach ( $users->get_results() as $item ) {
302 $items[] = $this->prepare_item( $item->ID );
303 }
304
305 $data = [
306 'success' => 1,
307 'status' => 200,
308 'data' => [
309 'total' => $users->get_total(),
310 'items' => $items,
311 ],
312 ];
313
314 return rest_ensure_response( $data );
315 }
316
317 /**
318 * Create staff
319 *
320 * @param WP_Rest_Request $request
321 *
322 * @return JSON | WP_Error
323 */
324 public function create_item( $request ) {
325 /**
326 * Added temporary for leagacy sass. It will remove in future.
327 */
328 $staff = Staff::all();
329
330 $response = [
331 'status_code' => 502,
332 'success' => 0,
333 'message' => esc_html__( 'Something went wrong', 'timetics' ),
334 ];
335
336 if ( apply_filters( 'timetics/staff/members/count_check', false, $staff ) == true ) {
337 return new WP_HTTP_Response( apply_filters( 'timetics/admin/staff/error_data', $response, 'count_check' ), 403 );
338 }
339
340 return $this->save_staff( $request );
341 }
342
343 /**
344 * Update staff
345 *
346 * @param WP_Rest_Request $request
347 *
348 * @return JSON | WP_Error
349 */
350 public function update_item( $request ) {
351 $staff_id = (int) $request['staff_id'];
352 $staff = new Staff( $staff_id );
353
354 if ( ! $staff->is_staff() ) {
355 $data = [
356 'status_code' => 404,
357 'message' => esc_html__( 'Invalid staff id.', 'timetics' ),
358 'data' => [],
359 ];
360
361 return new WP_HTTP_Response( $data, 404 );
362 }
363
364 $payload = json_decode( $request->get_body(), true );
365
366 if ( !empty($payload['schedule']) && $payload['schedule'] && apply_filters('timetics/staff/member/availability', false)) {
367
368 $response = [
369 'status_code' => 502,
370 'success' => 0,
371 'message' => esc_html__( 'Something went wrong', 'timetics' ),
372 ];
373
374 return new WP_HTTP_Response( apply_filters( 'timetics/admin/staff/error_data', $response, 'availability_update' ), 403 );
375 }
376
377 return $this->save_staff( $request, $staff_id );
378 }
379
380 /**
381 * Delete staff
382 *
383 * @param WP_Rest_Request $request
384 *
385 * @return JSON
386 */
387 public function delete_item( $request ) {
388 $staff_id = (int) $request['staff_id'];
389 $staff = new Staff( $staff_id );
390
391 if ( ! $staff->is_staff() ) {
392 $data = [
393 'status_code' => 404,
394 'message' => esc_html__( 'Invalid staff id.', 'timetics' ),
395 'data' => [],
396 ];
397
398 return new WP_HTTP_Response( $data, 404 );
399 }
400
401 $staff->delete();
402
403 $response = [
404 'status_code' => 200,
405 'message' => esc_html__( 'Successfully deleted staff', 'timetics' ),
406 'data' => [],
407 ];
408
409 return rest_ensure_response( $response );
410 }
411
412 /**
413 * Delete multiples
414 *
415 * @param WP_Rest_Request $request
416 *
417 * @return JSON
418 */
419 public function bulk_delete( $request ) {
420 $staffs = json_decode( $request->get_body(), true );
421
422 foreach ( $staffs as $staff ) {
423 $staff = new Staff( $staff );
424
425 if ( ! $staff->is_staff() ) {
426 $data = [
427 'success' => 1,
428 'status' => 404,
429 'message' => esc_html__( 'Invalid staff id.', 'timetics' ),
430 'data' => [],
431 ];
432
433 return new WP_HTTP_Response( $data, 404 );
434 }
435
436 $staff->delete();
437 }
438
439 /**
440 * Added temporary for leagacy sass. It will remove in future.
441 */
442 do_action( 'timetics/admin/staff/bulk_delete', $staffs );
443
444 return [
445 'success' => 1,
446 'status' => 200,
447 'message' => esc_html__( 'Successfully deleted staff', 'timetics' ),
448 'data' => [
449 'items' => $staffs,
450 ],
451 ];
452 }
453
454 /**
455 * Get staff integrations
456 *
457 * @param WP_Rest_Request $request
458 *
459 * @return JSON
460 */
461 public function get_integrations( $request ) {
462 $staff_id = (int) $request['staff_id'];
463 $staff = new Staff( $staff_id );
464
465 if ( ! $staff->is_staff() ) {
466 $data = [
467 'status_code' => 404,
468 'message' => esc_html__( 'Invalid staff id.', 'timetics' ),
469 'data' => [],
470 ];
471
472 return new WP_HTTP_Response( $data, 404 );
473 }
474
475 $integrations = timetics_get_staff_integrations( $staff_id );
476
477 return rest_ensure_response( $integrations );
478 }
479
480 /**
481 * Remove staff authentication for integrations
482 *
483 * @param WP_Rest_Request $request
484 *
485 * @return JSON
486 */
487 public function auth_revoke( $request ) {
488 $staff_id = (int) $request['staff_id'];
489 $staff = new Staff( $staff_id );
490 $integration = ! empty( $request['integration_name'] ) ? sanitize_text_field( $request['integration_name'] ) : '';
491
492 if ( ! $staff->is_staff() ) {
493 $data = [
494 'status_code' => 404,
495 'message' => esc_html__( 'Invalid staff id.', 'timetics' ),
496 'data' => [],
497 ];
498
499 return new WP_HTTP_Response( $data, 404 );
500 }
501
502 $client = new Client();
503
504 $revoked = false;
505
506 switch( $integration ) {
507 case 'google-auth':
508 $refresh_token = timetics_get_google_refresh_token( $staff_id );
509 if ( ! empty( $refresh_token ) ) {
510 $client->revoke( $refresh_token );
511 }
512
513 // Always clear ALL local Google credentials so the account can be reconnected cleanly.
514 delete_user_meta( $staff_id, 'timetics_google_auth' );
515 delete_user_meta( $staff_id, 'timetics_google_refresh_token' );
516 delete_user_meta( $staff_id, 'timetics_google_auth_code' );
517 delete_user_meta( $staff_id, 'timetics_google_auth_error' );
518
519 $revoked = true;
520
521 break;
522 case 'zoom-auth':
523 $revoked = true;
524 update_user_meta( $staff_id, 'timetics_zoom_token', '' );
525 break;
526
527 case 'outlook-auth':
528 $revoked = true;
529 update_user_meta( $staff_id, 'timetics_outlook_token', '');
530 break;
531 }
532
533 if ( ! $revoked ) {
534 $data = [
535 'success' => 0,
536 'status_code' => 409,
537 'message' => esc_html__( 'Something went wrong, please try again', 'timetics' ),
538 ];
539
540 return new WP_HTTP_Response( $data, 409 );
541 }
542
543 return [
544 'success' => 1,
545 'status_code' => 200,
546 'message' => esc_html__( 'Successfully disconnected', 'timetics' ),
547 ];
548 }
549
550 /**
551 * Save staff
552 *
553 * @param WP_Rest_Request $request
554 * @param integer $id [$id description]
555 *
556 * @return JSON | WP_Error
557 */
558 public function save_staff( $request, $id = 0 ) {
559 $data = json_decode( $request->get_body(), true );
560
561 $first_name = ! empty( $data['first_name'] ) ? sanitize_text_field( $data['first_name'] ) : '';
562 $last_name = ! empty( $data['last_name'] ) ? sanitize_text_field( $data['last_name'] ) : '';
563 $email = ! empty( $data['email'] ) ? $data['email'] : '';
564 $phone = ! empty( $data['phone'] ) ? $data['phone'] : '';
565 $service = ! empty( $data['service'] ) ? $data['service'] : [];
566 $schedule = ! empty( $data['schedule'] ) ? $data['schedule'] : [];
567 $image = ! empty( $data['image'] ) ? $data['image'] : '';
568 $password = ! empty( $data['password'] ) ? sanitize_text_field( $data['password'] ) : '';
569 $current_password = ! empty( $data['current_password'] ) ? sanitize_text_field( $data['current_password'] ) : '';
570 $action = $id ? 'updated' : 'created';
571
572 // Validate input data.
573 $validate = $this->validate(
574 $data, [
575 'first_name',
576 'email',
577 ]
578 );
579
580 if ( is_wp_error( $validate ) ) {
581 $data = [
582 'success' => 0,
583 'status_code' => 409,
584 'message' => $validate->get_error_messages(),
585 'data' => [],
586 ];
587
588 return new WP_HTTP_Response( $data, 409 );
589 }
590
591 // Save staff data.
592 $staff = new Staff( $id );
593
594 $args = [
595 'first_name' => $first_name,
596 'last_name' => $last_name,
597 'user_email' => $email,
598 'user_login' => $this->generate_username( $email ),
599 'phone' => $phone,
600 'image' => $image,
601 'schedule' => $schedule,
602 'user_pass' => $password,
603 ];
604
605 if ( $id ) {
606 if ( $password && ! wp_check_password( $current_password, $staff->get_password(), $id ) ) {
607 $data = [
608 'success' => 0,
609 'status_code' => 409,
610 'message' => esc_html__( 'Current password does not match', 'timetics' ),
611 ];
612
613 return new WP_HTTP_Response( $data, 400 );
614 }
615
616 $staff_id = $staff->update( $args );
617 } else {
618 $staff_id = $staff->create( $args );
619 }
620
621 if ( is_wp_error( $staff_id ) ) {
622 return [
623 'success' => 0,
624 'status' => 409,
625 'message' => $staff_id->get_error_message(),
626 'data' => [],
627 ];
628 }
629 // Prepare for response.
630 $item = $this->prepare_item( $staff );
631
632 /**
633 * Added temporary for leagacy sass. It will remove in future.
634 */
635 do_action( 'timetics/admin/staff/create_item', $item );
636
637 $data = [
638 'success' => 1,
639 'status' => 200,
640 /* translators: Action */
641 'message' => sprintf( esc_html__( 'Successfully %s staff', 'timetics' ), $action ),
642 'data' => $item,
643 ];
644
645 return rest_ensure_response( $data );
646 }
647
648 /**
649 * Generate username from email
650 *
651 * @param string $email
652 *
653 * @return string
654 */
655 public function generate_username( $email ) {
656 $username = strtok( $email, '@' );
657
658 if ( username_exists( $username ) ) {
659 $username = $username . wp_rand( 10, 100 );
660 }
661
662 return $username;
663 }
664
665 /**
666 * Prepare item
667 *
668 * @param integer | Staff $staff_id Staff Id
669 *
670 * @return array staff data
671 */
672 public function prepare_item( $staff ) {
673 $staff = new Staff( $staff );
674 $data = $staff->get_data();
675
676 if ( ! current_user_can( 'manage_timetics' ) ) {
677 unset( $data['email'], $data['phone'], $data['user_name'] );
678 }
679
680 return $data;
681 }
682
683 /**
684 * Get items permission callback
685 *
686 * @param WP_Rest_Request $request
687 *
688 * @return boolean true if user has permission, false otherwise
689 */
690 public function get_staffs_permission_callback($request){
691 $nonce = $request->get_header('X-WP-Nonce');
692 if (wp_verify_nonce($nonce, 'wp_rest') && ( current_user_can( 'manage_timetics' ) || current_user_can( 'manage_options' ) ) ) {
693 return true;
694 }
695 return false;
696 }
697 }
698