PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.64
Timetics – Appointment Booking Calendar & Scheduling v1.0.64
1.0.64 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 All 65 releases
← All changes | core/customers/api-customer.php +217 -34 1.0.2 → 1.0.64 View file →
@@ -5,9 +5,12 @@
5 5 * @package Timetics
6 6 */
7 7 namespace Timetics\Core\Customers;
8 8
9 +defined( 'ABSPATH' ) || exit;
10 +
9 11 use Timetics\Base\Api;
12 +use Timetics\Core\Bookings\Api_Booking;
10 13 use Timetics\Utils\Singleton;
11 14 use WP_HTTP_Response;
12 15 use WP_User_Query;
13 16
@@ -40,9 +43,9 @@
40 43 register_rest_route(
41 44 $this->namespace, $this->rest_base, [
42 45 [
43 46 'methods' => \WP_REST_Server::READABLE,
44 - 'callback' => [ $this, 'get_items' ],
47 + 'callback' => [$this, 'get_items'],
45 48 'permission_callback' => function () {
46 49 return current_user_can( 'manage_timetics' );
47 50 },
48 51 ],
@@ -47,9 +50,9 @@
47 50 },
48 51 ],
49 52 [
50 53 'methods' => \WP_REST_Server::CREATABLE,
51 - 'callback' => [ $this, 'create_item' ],
54 + 'callback' => [$this, 'create_item'],
52 55 'permission_callback' => function () {
53 56 return current_user_can( 'manage_timetics' );
54 57 },
55 58 ],
@@ -54,11 +57,11 @@
54 57 },
55 58 ],
56 59 [
57 60 'methods' => \WP_REST_Server::DELETABLE,
58 - 'callback' => [ $this, 'bulk_delete' ],
61 + 'callback' => [$this, 'bulk_delete'],
59 62 'permission_callback' => function () {
60 - return current_user_can( 'manage_timetics' );
63 + return current_user_can( 'manage_options' );
61 64 },
62 65 ],
63 66 ]
64 67 );
@@ -71,23 +74,42 @@
71 74 register_rest_route(
72 75 $this->namespace, '/' . $this->rest_base . '/(?P<customer_id>[\d]+)', [
73 76 [
74 77 'methods' => \WP_REST_Server::READABLE,
75 - 'callback' => [ $this, 'get_item' ],
76 - 'permission_callback' => function () {
77 - return current_user_can( 'manage_timetics' );
78 + 'callback' => [$this, 'get_item'],
79 + 'permission_callback' => function ( $request ) {
80 + $customer_id = (int) $request['customer_id'];
81 +
82 + if ( timetics_can_view_all_data() || get_current_user_id() === $customer_id ) {
83 + return true;
84 + }
85 +
86 + // manage_timetics is not admin-only — staff may only view a
87 + // customer they actually have a visible booking with.
88 + return current_user_can( 'manage_timetics' )
89 + && in_array( $customer_id, timetics_get_visible_customer_ids(), true );
78 90 },
79 91 ],
80 92 [
81 93 'methods' => \WP_REST_Server::EDITABLE,
82 - 'callback' => [ $this, 'update_item' ],
94 + 'callback' => [$this, 'update_item'],
95 + 'permission_callback' => [$this, 'update_item_permission_callback'],
96 + ],
97 + [
98 + 'methods' => \WP_REST_Server::DELETABLE,
99 + 'callback' => [$this, 'delete_item'],
83 100 'permission_callback' => function () {
84 - return current_user_can( 'manage_timetics' );
101 + return current_user_can( 'manage_options' );
85 102 },
86 103 ],
104 + ]
105 + );
106 +
107 + register_rest_route(
108 + $this->namespace, $this->rest_base . '/search', [
87 109 [
88 - 'methods' => \WP_REST_Server::DELETABLE,
89 - 'callback' => [ $this, 'delete_item' ],
110 + 'methods' => \WP_REST_Server::READABLE,
111 + 'callback' => [$this, 'search_items'],
90 112 'permission_callback' => function () {
91 113 return current_user_can( 'manage_timetics' );
92 114 },
93 115 ],
@@ -93,19 +115,32 @@
93 115 ],
94 116 ]
95 117 );
96 118
119 + /**
120 + * Register route for booking single customer
121 + *
122 + * @var void
123 + */
97 124 register_rest_route(
98 - $this->namespace, $this->rest_base . '/search', [
125 + $this->namespace, '/' . $this->rest_base . '/(?P<customer_id>[\d]+)/bookings', [
99 126 [
100 127 'methods' => \WP_REST_Server::READABLE,
101 - 'callback' => [ $this, 'search_items' ],
102 - 'permission_callback' => function () {
103 - return current_user_can( 'edit_posts' );
128 + 'callback' => [$this, 'get_bookings'],
129 + 'permission_callback' => function ( $request ) {
130 + $customer_id = (int) $request['customer_id'];
131 +
132 + if ( timetics_can_view_all_data() || get_current_user_id() === $customer_id ) {
133 + return true;
134 + }
135 +
136 + return current_user_can( 'manage_timetics' )
137 + && in_array( $customer_id, timetics_get_visible_customer_ids(), true );
104 138 },
105 139 ],
106 140 ]
107 141 );
142 +
108 143 }
109 144 /**
110 145 * Get all customers
111 146 *
@@ -116,15 +151,20 @@
116 151 public function get_items( $request ) {
117 152 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
118 153 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
119 154
120 - $customer = Customer::all(
121 - [
122 - 'number' => $per_page,
123 - 'paged' => $paged,
124 - ]
125 - );
155 + $args = [
156 + 'number' => $per_page,
157 + 'paged' => $paged,
158 + ];
126 159
160 + // Staff only see customers from their own bookings, administrators see everyone.
161 + if ( ! timetics_can_view_all_data() ) {
162 + $args['include'] = timetics_get_visible_customer_ids( get_current_user_id() );
163 + }
164 +
165 + $customer = Customer::all( $args );
166 +
127 167 $items = [];
128 168
129 169 foreach ( $customer['items'] as $item ) {
130 170 $items[] = $this->prepare_item( $item->ID );
@@ -129,8 +169,13 @@
129 169 foreach ( $customer['items'] as $item ) {
130 170 $items[] = $this->prepare_item( $item->ID );
131 171 }
132 172
173 + /**
174 + * Added temporary for leagacy sass. It will remove in future.
175 + */
176 + $items = apply_filters( 'timetics/admin/customer/get_items', $items );
177 +
133 178 $data = [
134 179 'success' => 1,
135 180 'data' => [
136 181 'total' => $customer['total'],
@@ -154,16 +199,21 @@
154 199
155 200 if ( ! $customer->is_customer() ) {
156 201 return [
157 202 'status_code' => 404,
158 - 'message' => __( 'Invalid customer id.', 'timetics' ),
203 + 'message' => esc_html__( 'Invalid customer id.', 'timetics' ),
159 204 'data' => [],
160 205 ];
161 206 }
162 207
208 + /**
209 + * Added temporary for leagacy sass. It will remove in future.
210 + */
211 + $item = apply_filters( 'timetics/admin/customer/get_item', $this->prepare_item( $customer ) );
212 +
163 213 $response = [
164 214 'status_code' => 200,
165 - 'data' => $this->prepare_item( $customer ),
215 + 'data' => $item,
166 216 ];
167 217
168 218 return rest_ensure_response( $response );
169 219 }
@@ -179,15 +229,24 @@
179 229 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
180 230 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
181 231 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
182 232
233 + $query_args = array(
234 + 'role' => 'timetics-customer',
235 + 'number' => $per_page,
236 + 'paged' => $paged,
237 + );
238 +
239 + // Staff only see customers from their own bookings, administrators see everyone.
240 + if ( ! timetics_can_view_all_data() ) {
241 + $query_args['include'] = timetics_get_visible_customer_ids( get_current_user_id() );
242 + }
243 +
183 244 // Get search.
184 245 $users = new WP_User_Query(
185 - array(
186 - 'role' => 'timetics-customer',
187 - 'number' => $per_page,
188 - 'paged' => $paged,
189 -
246 + array_merge(
247 + $query_args,
248 + array(
190 249 // @codingStandardsIgnoreStart
191 250 'meta_query' => array(
192 251 'relation' => 'OR',
193 252 array(
@@ -211,8 +270,9 @@
211 270 'compare' => 'LIKE',
212 271 ),
213 272 ),
214 273 // @codingStandardsIgnoreEnd
274 + )
215 275 )
216 276 );
217 277
218 278 // Prepare items for response.
@@ -245,8 +305,37 @@
245 305 return $this->save_customer( $request );
246 306 }
247 307
248 308 /**
309 + * Update customer Permission check
310 + *
311 + * @param WP_Rest_Request $request
312 + *
313 + * @return JSON | WP_Error
314 + */
315 + public function update_item_permission_callback( $request ) {
316 + $customer_id = (int) $request['customer_id'];
317 +
318 + // Admins can always update any customer. manage_timetics is not
319 + // admin-only — every timetics-staff account has it — so it must not
320 + // grant edit access to someone else's customer record.
321 + if ( current_user_can( 'manage_options' ) ) {
322 + return true;
323 + }
324 +
325 + // Customers can update themselves with a valid nonce
326 + $nonce = $request->get_header( 'X-WP-Nonce' );
327 + if ( ! empty( $nonce ) && wp_verify_nonce( $nonce, 'wp_rest' ) ) {
328 + $current_user_id = get_current_user_id();
329 + if ( $customer_id === $current_user_id && $current_user_id > 0 ) {
330 + return true;
331 + }
332 + }
333 +
334 + return false;
335 + }
336 +
337 + /**
249 338 * Update customer
250 339 *
251 340 * @param WP_Rest_Request $request
252 341 *
@@ -258,9 +347,9 @@
258 347
259 348 if ( ! $customer->is_customer() ) {
260 349 $data = [
261 350 'status_code' => 404,
262 - 'message' => __( 'Invalid customer id.', 'timetics' ),
351 + 'message' => esc_html__( 'Invalid customer id.', 'timetics' ),
263 352 'data' => [],
264 353 ];
265 354
266 355 return new WP_HTTP_Response( $data, 404 );
@@ -282,9 +371,9 @@
282 371
283 372 if ( ! $customer->is_customer() ) {
284 373 $data = [
285 374 'status_code' => 404,
286 - 'message' => __( 'Invalid customer id.', 'timetics' ),
375 + 'message' => esc_html__( 'Invalid customer id.', 'timetics' ),
287 376 'data' => [],
288 377 ];
289 378
290 379 return new WP_HTTP_Response( $data, 404 );
@@ -291,11 +380,16 @@
291 380 }
292 381
293 382 $customer->delete();
294 383
384 + /**
385 + * Added temporary for leagacy sass. It will remove in future.
386 + */
387 + do_action( 'timetics/admin/customer/delete_item', $customer );
388 +
295 389 $response = [
296 390 'status_code' => 200,
297 - 'message' => __( 'Successfully deleted customer', 'timetics' ),
391 + 'message' => esc_html__( 'Successfully deleted customer', 'timetics' ),
298 392 'data' => [],
299 393 ];
300 394
301 395 return rest_ensure_response( $response );
@@ -317,9 +411,9 @@
317 411 if ( ! $customer->is_customer() ) {
318 412 $data = [
319 413 'success' => 1,
320 414 'status' => 404,
321 - 'message' => __( 'Invalid customer id.', 'timetics' ),
415 + 'message' => esc_html__( 'Invalid customer id.', 'timetics' ),
322 416 'data' => [],
323 417 ];
324 418
325 419 return new WP_HTTP_Response( $data, 404 );
@@ -327,12 +421,17 @@
327 421
328 422 $customer->delete();
329 423 }
330 424
425 + /**
426 + * Added temporary for leagacy sass. It will remove in future.
427 + */
428 + do_action( 'timetics/admin/customer/bulk_delete', $customers );
429 +
331 430 return [
332 431 'success' => 1,
333 432 'status' => 200,
334 - 'message' => __( 'Successfully deleted customer', 'timetics' ),
433 + 'message' => esc_html__( 'Successfully deleted customer', 'timetics' ),
335 434 'data' => [
336 435 'items' => $customers,
337 436 ],
338 437 ];
@@ -353,8 +452,9 @@
353 452 $last_name = ! empty( $data['last_name'] ) ? $data['last_name'] : '';
354 453 $email = ! empty( $data['email'] ) ? $data['email'] : '';
355 454 $phone = ! empty( $data['phone'] ) ? $data['phone'] : '';
356 455 $image = ! empty( $data['image'] ) ? intval( $data['image'] ) : '';
456 + $password = ! empty( $data['password'] ) ? $data['password'] : '';
357 457 $action = $id ? 'updated' : 'created';
358 458
359 459 // Validate input data.
360 460 $validate = $this->validate(
@@ -386,8 +486,9 @@
386 486 'email' => $email,
387 487 'user_name' => $this->generate_username( $email ),
388 488 'phone' => $phone,
389 489 'image' => $image,
490 + 'password' => $password,
390 491 ]
391 492 );
392 493
393 494 $customer->save();
@@ -402,13 +503,18 @@
402 503 }
403 504 // Prepare for response.
404 505 $item = $this->prepare_item( $customer );
405 506
507 + /**
508 + * Added temporary for leagacy sass. It will remove in future.
509 + */
510 + do_action( 'timetics/admin/customer/create_item', $item );
511 +
406 512 $data = [
407 513 'success' => 1,
408 514 'status' => 200,
409 515 /* translators: Action */
410 - 'message' => sprintf( __( 'Successfully %s customer', 'timetics' ), $action ),
516 + 'message' => sprintf( esc_html__( 'Successfully %s customer', 'timetics' ), $action ),
411 517 'data' => $item,
412 518 ];
413 519
414 520 return rest_ensure_response( $data );
@@ -450,7 +556,84 @@
450 556 'phone' => $customer->get_phone(),
451 557 'image' => $customer->get_image(),
452 558 'total_booking' => $customer->get_total_booking(),
453 559 ];
560 + }
561 +
562 + /**
563 + * Get booking list for single customer
564 + *
565 + * @param integer | customer $customer_id customer Id
566 + *
567 + * @return array booking data
568 + */
569 + public function get_bookings( $request ) {
570 + $customer_id = (int) $request['customer_id'];
571 + $customer = new Customer( $customer_id );
572 +
573 + if ( ! $customer->is_customer() ) {
574 + $data = [
575 + 'status_code' => 404,
576 + 'message' => esc_html__( 'Invalid customer id.', 'timetics' ),
577 + 'data' => [],
578 + ];
579 +
580 + return new WP_HTTP_Response( $data, 404 );
581 + }
582 +
583 + return $this->get_booking_list( $request, $customer_id );
584 + }
585 +
586 + /**
587 + * Save customer
588 + *
589 + * @param WP_Rest_Request $request
590 + * @param integer $id [$id description]
591 + *
592 + * @return JSON | WP_Error
593 + */
594 + public function get_booking_list( $request, $customer_id = 0 ) {
595 +
596 + $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
597 + $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
598 +
599 + $args = [
600 + 'post_type' => 'timetics-booking',
601 + 'posts_per_page' => $per_page,
602 + 'paged' => $paged,
603 + 'post_status' => 'any',
604 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering bookings by customer
605 + 'meta_query' => array(
606 + 'relation' => 'OR',
607 + array(
608 + 'key' => '_tt_booking_customer',
609 + 'value' => $customer_id,
610 + 'compare' => '=',
611 + ),
612 +
613 + ),
614 + ];
615 +
616 + $bookings = new \WP_Query( $args );
617 +
618 + // Prepare items for response.
619 + $items = [];
620 + $booking = new Api_Booking();
621 +
622 + foreach ( $bookings->posts as $item ) {
623 + $items[] = $booking->prepare_item( $item->ID, false );
624 + }
625 +
626 + $data = [
627 + 'success' => 1,
628 + 'status' => 200,
629 + 'data' => [
630 + 'total' => $bookings->found_posts,
631 + 'items' => $items,
632 + ],
633 + ];
634 +
635 + return rest_ensure_response( $data );
636 +
454 637 }
455 638
456 639 }