PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.3.1
LatePoint – Calendar Booking Plugin for Appointments and Events v5.3.1
5.6.7 5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / controllers / customer_cabinet_controller.php
latepoint / lib / controllers Last commit date
activities_controller.php 3 months ago auth_controller.php 3 months ago booking_form_settings_controller.php 3 months ago bookings_controller.php 3 months ago calendars_controller.php 3 months ago carts_controller.php 3 months ago controller.php 3 months ago customer_cabinet_controller.php 3 months ago customers_controller.php 3 months ago dashboard_controller.php 3 months ago default_agent_controller.php 3 months ago events_controller.php 3 months ago form_fields_controller.php 3 months ago integrations_controller.php 3 months ago invoices_controller.php 3 months ago manage_booking_by_key_controller.php 3 months ago manage_order_by_key_controller.php 3 months ago notifications_controller.php 3 months ago orders_controller.php 3 months ago pro_controller.php 3 months ago process_jobs_controller.php 3 months ago processes_controller.php 3 months ago search_controller.php 3 months ago services_controller.php 3 months ago settings_controller.php 3 months ago steps_controller.php 3 months ago stripe_connect_controller.php 3 months ago support_topics_controller.php 3 months ago todos_controller.php 3 months ago transactions_controller.php 3 months ago wizard_controller.php 3 months ago
customer_cabinet_controller.php
585 lines
1 <?php
2 /*
3 * Copyright (c) 2023 LatePoint LLC. All rights reserved.
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit; // Exit if accessed directly.
8 }
9
10
11 if ( ! class_exists( 'OsCustomerCabinetController' ) ) :
12
13
14 class OsCustomerCabinetController extends OsController {
15
16 function __construct() {
17 parent::__construct();
18
19 $this->action_access['customer'] = array_merge(
20 $this->action_access['customer'],
21 [
22 'update',
23 'request_cancellation',
24 'print_booking_info',
25 'print_order_info',
26 'ical_download',
27 'process_reschedule_request',
28 'request_reschedule_calendar',
29 'view_order_summary_in_lightbox',
30 'view_booking_summary_in_lightbox',
31 'scheduling_summary_for_bundle',
32 'reload_booking_tile',
33 ]
34 );
35 $this->action_access['public'] = array_merge(
36 $this->action_access['public'],
37 [
38 'logout',
39 'dashboard',
40 'login',
41 'do_login',
42 'password_reset_form',
43 'request_password_reset_token',
44 'change_password',
45 'set_account_password_on_booking_completion',
46 ]
47 );
48 $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'customer_cabinet/';
49 }
50
51
52 public function scheduling_summary_for_bundle() {
53 if ( ! filter_var( $this->params['order_item_id'], FILTER_VALIDATE_INT ) ) {
54 exit();
55 }
56 $order_item = new OsOrderItemModel( $this->params['order_item_id'] );
57 $order = new OsOrderModel( $order_item->order_id );
58
59 if ( $order->is_new_record() || ( $order->customer_id != OsAuthHelper::get_logged_in_customer_id() ) ) {
60 $this->send_json(
61 array(
62 'status' => LATEPOINT_STATUS_ERROR,
63 'message' => __( 'Not Allowed', 'latepoint' ),
64 )
65 );
66 }
67
68 $bundle = $order_item->build_original_object_from_item_data();
69 $this->vars['order_item'] = $order_item;
70 $this->vars['bundle'] = $bundle;
71 $this->format_render( __FUNCTION__ );
72 }
73
74 public function view_order_summary_in_lightbox() {
75 if ( ! filter_var( $this->params['order_id'], FILTER_VALIDATE_INT ) ) {
76 exit();
77 }
78 $order = new OsOrderModel( $this->params['order_id'] );
79
80 if ( $order->is_new_record() || ( $order->customer_id != OsAuthHelper::get_logged_in_customer_id() ) ) {
81 $this->send_json(
82 array(
83 'status' => LATEPOINT_STATUS_ERROR,
84 'message' => __( 'Not Allowed', 'latepoint' ),
85 )
86 );
87 }
88
89 $this->vars['order'] = $order;
90 $this->vars['price_breakdown_rows'] = $order->generate_price_breakdown_rows();
91 $this->format_render( __FUNCTION__ );
92 }
93
94 public function view_booking_summary_in_lightbox() {
95 if ( ! filter_var( $this->params['booking_id'], FILTER_VALIDATE_INT ) ) {
96 exit();
97 }
98 $booking = new OsBookingModel( $this->params['booking_id'] );
99 $order_item = new OsOrderItemModel( $booking->order_item_id );
100 $order = new OsOrderModel( $order_item->order_id );
101
102 if ( $order->is_new_record() || ( $order->customer_id != OsAuthHelper::get_logged_in_customer_id() ) ) {
103 $this->send_json(
104 array(
105 'status' => LATEPOINT_STATUS_ERROR,
106 'message' => __( 'Not Allowed', 'latepoint' ),
107 )
108 );
109 }
110
111 $this->vars['booking'] = $booking;
112 $this->vars['order_item'] = $order_item;
113 $this->vars['order'] = $order;
114 $this->format_render( __FUNCTION__ );
115 }
116
117
118 function print_order_info() {
119 if ( ! filter_var( $this->params['latepoint_order_id'], FILTER_VALIDATE_INT ) ) {
120 exit();
121 }
122 $order_id = $this->params['latepoint_order_id'];
123 if ( empty( $order_id ) ) {
124 return;
125 }
126 $order = new OsOrderModel( $order_id );
127 if ( $order->id && OsAuthHelper::is_customer_logged_in() && ( $order->customer_id == OsAuthHelper::get_logged_in_customer_id() ) ) {
128 $customer = $order->customer;
129 $this->vars['order'] = $order;
130 $this->vars['customer'] = $customer;
131 $this->set_layout( 'print' );
132 $content = $this->format_render_return( __FUNCTION__, [], [], true );
133 echo $content;
134 }
135 }
136
137 function print_booking_info() {
138 if ( ! filter_var( $this->params['latepoint_booking_id'], FILTER_VALIDATE_INT ) ) {
139 exit();
140 }
141 $booking_id = $this->params['latepoint_booking_id'];
142 if ( empty( $booking_id ) ) {
143 return;
144 }
145 $booking = new OsBookingModel( $booking_id );
146 if ( $booking->id && OsAuthHelper::is_customer_logged_in() && ( $booking->customer_id == OsAuthHelper::get_logged_in_customer_id() ) ) {
147 $customer = $booking->customer;
148 $this->vars['booking'] = $booking;
149 $this->vars['customer'] = $customer;
150 $this->set_layout( 'print' );
151 $content = $this->format_render_return( __FUNCTION__, [], [], true );
152 echo $content;
153 }
154 }
155
156 function ical_download() {
157 if ( ! filter_var( $this->params['latepoint_booking_id'], FILTER_VALIDATE_INT ) ) {
158 exit();
159 }
160 $booking_id = $this->params['latepoint_booking_id'];
161 if ( empty( $booking_id ) ) {
162 return;
163 }
164 $booking = new OsBookingModel( $booking_id );
165 if ( $booking->id && OsAuthHelper::is_customer_logged_in() && ( $booking->customer_id == OsAuthHelper::get_logged_in_customer_id() ) ) {
166
167 header( 'Content-Type: text/calendar; charset=utf-8' );
168 header( 'Content-Disposition: attachment; filename=booking_' . $booking->id . '.ics' );
169
170 echo OsBookingHelper::generate_ical_event_string( $booking );
171 }
172 }
173
174
175 function process_reschedule_request() {
176 if ( ! filter_var( $this->params['booking_id'], FILTER_VALIDATE_INT ) ) {
177 exit();
178 }
179 $booking = new OsBookingModel( $this->params['booking_id'] );
180
181 if ( empty( $booking->id ) || empty( $this->params['start_date'] ) || empty( $this->params['start_time'] ) ) {
182 return;
183 }
184
185 if ( ( OsAuthHelper::get_logged_in_customer_id() == $booking->customer_id ) && OsCustomerHelper::can_reschedule_booking( $booking ) ) {
186 $old_booking = clone $booking;
187 $booking->start_date = $this->params['start_date'];
188 $booking->start_time = $this->params['start_time'];
189
190 $booking->convert_start_datetime_into_server_timezone( $booking->get_customer_timezone_name() );
191
192 if ( $booking->is_start_date_and_time_set() ) {
193 $booking->calculate_end_date_and_time();
194 $booking->set_utc_datetimes();
195 }
196
197 // check if booking time is still available
198 if ( ! OsBookingHelper::is_booking_request_available( \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ), [ 'exclude_booking_ids' => [ $booking->id ] ] ) ) {
199 $response_html = __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' );
200 $status = LATEPOINT_STATUS_ERROR;
201 } else {
202 if ( OsSettingsHelper::is_on( 'change_status_on_customer_reschedule' ) ) {
203 $allowed_statuses = OsBookingHelper::get_statuses_list();
204 if ( isset( $allowed_statuses[ OsSettingsHelper::get_settings_value( 'status_to_set_after_customer_reschedule' ) ] ) ) {
205 $booking->status = OsSettingsHelper::get_settings_value( 'status_to_set_after_customer_reschedule' );
206 }
207 }
208 if ( $booking->save() ) {
209 /**
210 * Booking is updated
211 *
212 * @param {OsBookingModel} $this->>booking Updated instance of booking model
213 * @param {OsBookingModel} $old_booking Instance of booking model before it was updated
214 *
215 * @since 4.9.0
216 * @hook latepoint_booking_updated
217 *
218 */
219 do_action( 'latepoint_booking_updated', $booking, $old_booking );
220 $this->vars['booking'] = $booking;
221 $this->vars['timezone_name'] = OsTimeHelper::get_timezone_name_from_session();
222 $this->vars['viewer'] = 'customer';
223 $status = LATEPOINT_STATUS_SUCCESS;
224 $this->set_layout( 'none' );
225 $response_html = $this->format_render_return( __FUNCTION__, [], [], true );
226 } else {
227 OsDebugHelper::log( 'Error rescheduling appointment', 'booking_reschedule_error', $booking->get_error_messages() );
228 $response_html = __( 'Error! Please try again later', 'latepoint' );
229 $status = LATEPOINT_STATUS_ERROR;
230 }
231 }
232 } else {
233 $status = LATEPOINT_STATUS_ERROR;
234 $response_html = __( 'Error! LKDFU343', 'latepoint' );
235 }
236
237 if ( $this->get_return_format() == 'json' ) {
238 $this->send_json(
239 array(
240 'status' => $status,
241 'message' => $response_html,
242 )
243 );
244 }
245 }
246
247 function request_reschedule_calendar() {
248 if ( ! filter_var( $this->params['booking_id'], FILTER_VALIDATE_INT ) ) {
249 exit();
250 }
251 $booking = new OsBookingModel( $this->params['booking_id'] );
252
253 if ( ! empty( $booking->id ) && ( OsAuthHelper::get_logged_in_customer_id() == $booking->customer_id ) && OsCustomerHelper::can_reschedule_booking( $booking ) ) {
254 $this->vars['booking'] = $booking;
255 $this->vars['calendar_start_date'] = ! empty( $this->params['calendar_start_date'] ) ? new OsWpDateTime( $this->params['calendar_start_date'] ) : new OsWpDateTime( 'today' );
256 $this->vars['timezone_name'] = $booking->get_customer_timezone_name();
257
258 $this->set_layout( 'none' );
259 $response_html = $this->format_render_return( __FUNCTION__, [], [], true );
260 } else {
261 $status = LATEPOINT_STATUS_ERROR;
262 $response_html = __( 'Reschedule is not allowed', 'latepoint' );
263 }
264 if ( $this->get_return_format() == 'json' ) {
265 $this->send_json(
266 array(
267 'status' => $status,
268 'message' => $response_html,
269 )
270 );
271 }
272 }
273
274 function request_cancellation() {
275 if ( ! filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
276 exit();
277 }
278
279 $booking_id = $this->params['id'];
280 $booking = new OsBookingModel( $booking_id );
281 if ( ! empty( $booking->id ) && ( OsAuthHelper::get_logged_in_customer_id() == $booking->customer_id ) && OsCustomerHelper::can_cancel_booking( $booking ) ) {
282 if ( $booking->update_status( LATEPOINT_BOOKING_STATUS_CANCELLED ) ) {
283 $status = LATEPOINT_STATUS_SUCCESS;
284 $response_html = __( 'Appointment Status Updated', 'latepoint' );
285 } else {
286 $status = LATEPOINT_STATUS_ERROR;
287 $response_html = __( 'Error Updating Booking Status!', 'latepoint' ) . ' ' . implode( ',', $booking->get_error_messages() );
288 }
289 } else {
290 $status = LATEPOINT_STATUS_ERROR;
291 $response_html = __( 'Not allowed to cancel', 'latepoint' );
292 }
293 if ( $this->get_return_format() == 'json' ) {
294 $this->send_json(
295 array(
296 'status' => $status,
297 'message' => $response_html,
298 )
299 );
300 }
301 }
302
303 /*
304 Update profile
305 */
306
307 public function update() {
308 $customer = OsAuthHelper::get_logged_in_customer();
309 if ( ! $customer ) {
310 exit();
311 }
312 $this->check_nonce( 'update_customer_' . $customer->get_uuid() );
313
314
315 if ( $customer ) {
316 $old_customer_data = $customer->get_data_vars();
317 $customer->set_data( $this->params['customer'], LATEPOINT_PARAMS_SCOPE_CUSTOMER );
318 if ( $customer->save() ) {
319 $response_html = __( 'Information Saved', 'latepoint' );
320 $status = LATEPOINT_STATUS_SUCCESS;
321 do_action( 'latepoint_customer_updated', $customer, $old_customer_data );
322 } else {
323 $response_html = $customer->get_error_messages();
324 $status = LATEPOINT_STATUS_ERROR;
325 }
326 } else {
327 $response_html = __( 'Customer not found', 'latepoint' );
328 $status = LATEPOINT_STATUS_ERROR;
329 }
330 if ( $this->get_return_format() == 'json' ) {
331 $this->send_json(
332 array(
333 'status' => $status,
334 'message' => $response_html,
335 )
336 );
337 }
338 }
339
340 public function reload_booking_tile() {
341 if ( ! filter_var( $this->params['booking_id'], FILTER_VALIDATE_INT ) ) {
342 exit();
343 }
344
345 $booking_id = $this->params['booking_id'];
346 $booking = new OsBookingModel( $booking_id );
347
348 if ( $booking->id && OsAuthHelper::get_logged_in_customer_id() == $booking->customer_id ) {
349 $this->vars['booking'] = $booking;
350 $this->vars['is_upcoming_booking'] = $booking->is_upcoming();
351 $this->set_layout( 'none' );
352 $response_html = $this->format_render_return( '_booking_tile' );
353 $status = LATEPOINT_STATUS_SUCCESS;
354 } else {
355 $response_html = __( 'Invalid Booking', 'latepoint' );
356 $status = LATEPOINT_STATUS_ERROR;
357 }
358
359 if ( $this->get_return_format() == 'json' ) {
360 $this->send_json(
361 array(
362 'status' => $status,
363 'message' => $response_html,
364 )
365 );
366 }
367 }
368
369 public function logout() {
370 OsAuthHelper::logout_customer();
371 nocache_headers();
372 wp_redirect( OsSettingsHelper::get_customer_dashboard_url(), 302 );
373 }
374
375 public function login() {
376 $this->set_layout( 'none' );
377
378 return $this->format_render_return( __FUNCTION__ );
379 }
380
381 public function do_login() {
382 $customer = OsAuthHelper::login_customer( sanitize_email( $this->params['auth']['email'] ), $this->params['auth']['password'] );
383 if ( $customer ) {
384 $response_html = OsSettingsHelper::get_customer_dashboard_url();
385 $status = LATEPOINT_STATUS_SUCCESS;
386 } else {
387 $status = LATEPOINT_STATUS_ERROR;
388 $response_html = __( 'Invalid password or email', 'latepoint' );
389 }
390 if ( $this->get_return_format() == 'json' ) {
391 $this->send_json(
392 array(
393 'status' => $status,
394 'message' => $response_html,
395 )
396 );
397 }
398 }
399
400
401 public function password_reset_form() {
402 $this->vars['from_booking'] = ( isset( $this->params['from_booking'] ) && $this->params['from_booking'] );
403 $this->set_layout( 'none' );
404
405 return $this->format_render_return( __FUNCTION__ );
406 }
407
408 public function request_password_reset_token() {
409 $this->set_layout( 'none' );
410 $this->vars['from_booking'] = ( isset( $this->params['from_booking'] ) && $this->params['from_booking'] );
411
412 if ( isset( $this->params['password_reset_email'] ) ) {
413 $customer_model = new OsCustomerModel();
414 $customer = $customer_model->where( [ 'email' => sanitize_email( $this->params['password_reset_email'] ) ] )->set_limit( 1 )->get_results_as_models();
415 $customer_mailer = new OsCustomerMailer();
416
417 $customer->account_nonse = sha1( wp_rand( 10000, 99999 ) . time() . wp_generate_password( 32, true, true ) );
418 $customer->save();
419
420 if ( $customer && $customer_mailer->password_reset_request( $customer, $customer->account_nonse ) ) {
421 // Save timestamp for token expiration tracking (Unix timestamp for consistency)
422 OsMetaHelper::save_customer_meta_by_key( 'password_reset_token_created_at', current_time( 'timestamp' ), $customer->id );
423 return $this->format_render_return( 'password_reset_form' );
424 } else {
425 $this->vars['reset_token_error'] = ( $customer ) ? __( 'Error! Email was not sent.', 'latepoint' ) : __( 'Email does not match any customer', 'latepoint' );
426
427 return $this->format_render_return( __FUNCTION__ );
428 }
429 } else {
430 return $this->format_render_return( __FUNCTION__ );
431 }
432 }
433
434 public function dashboard( array $params = [] ) {
435 if ( ! OsAuthHelper::is_customer_logged_in() ) {
436 $this->set_layout( 'none' );
437
438 return $this->format_render_return( 'login' );
439 } else {
440 $customer = OsAuthHelper::get_logged_in_customer();
441 $this->vars['customer'] = $customer;
442 $this->vars['orders'] = $customer->get_orders();
443
444 $this->vars['future_bookings'] = $customer->get_future_bookings();
445 $this->vars['past_bookings'] = $customer->get_past_bookings();
446 $this->vars['cancelled_bookings'] = $customer->get_cancelled_bookings();
447 $this->vars['not_scheduled_bundles'] = $customer->get_not_scheduled_bundles();
448
449 $this->vars['cart_not_empty'] = ( ! OsCartsHelper::is_current_cart_empty() && OsCartsHelper::can_checkout_multiple_items() );
450
451 $this->vars['hide_new_appointment_ui'] = $params['hide_new_appointment_ui'] ?? false;
452
453 $this->set_layout( 'none' );
454
455 return $this->format_render_return( __FUNCTION__ );
456 }
457 }
458
459 public function change_password() {
460 $params = OsParamsHelper::permit_params(
461 $this->params,
462 [
463 'password_reset_token',
464 'password',
465 'password_confirmation',
466 'change_password_nonce',
467 ]
468 );
469
470 if ( empty( $params['password'] ) ) {
471 $this->send_json(
472 array(
473 'status' => LATEPOINT_STATUS_ERROR,
474 'message' => __( 'Password can not be blank', 'latepoint' ),
475 )
476 );
477 }
478
479
480 $customer = false;
481 if ( OsAuthHelper::is_customer_logged_in() ) {
482 $this->check_nonce( 'change_password_' . OsAuthHelper::get_logged_in_customer_uuid(), $params['change_password_nonce'] );
483 $customer = OsAuthHelper::get_logged_in_customer();
484 } elseif ( ! empty( $params['password_reset_token'] ) ) {
485 $params['password_reset_token'] = sanitize_text_field( $params['password_reset_token'] );
486 $customer = OsCustomerHelper::get_by_account_nonse( $params['password_reset_token'] );
487
488 // Validate token expiration (15 minutes)
489 if ( $customer ) {
490 $created_at = OsMetaHelper::get_customer_meta_by_key( 'password_reset_token_created_at', $customer->id );
491
492 if ( empty( $created_at ) ) {
493 // No timestamp - reject for security (token created before fix)
494 $customer = false;
495 $status = LATEPOINT_STATUS_ERROR;
496 $response_html = __( 'Password token is invalid. Please request a new password reset.', 'latepoint' );
497 } else {
498 $age_seconds = current_time( 'timestamp' ) - intval( $created_at );
499 if ( $age_seconds > 900 ) { // 15 minutes = 900 seconds
500 // Clean up expired meta
501 OsMetaHelper::delete_customer_meta_by_key( 'password_reset_token_created_at', $customer->id );
502 $customer = false;
503 $status = LATEPOINT_STATUS_ERROR;
504 $response_html = __( 'Password token has expired. Please request a new password reset.', 'latepoint' );
505 }
506 }
507 }
508 }
509 if ( $customer ) {
510 if ( ! empty( $params['password'] ) && $params['password'] == $params['password_confirmation'] ) {
511 if ( $customer->update_password( $params['password'] ) ) {
512 // Invalidate token after successful password reset
513 if ( ! empty( $params['password_reset_token'] ) ) {
514 OsCustomerHelper::invalidate_password_reset_token( $customer );
515 }
516 $status = LATEPOINT_STATUS_SUCCESS;
517 $response_html = __( 'Your password was successfully updated.', 'latepoint' );
518 } else {
519 $response_html = __( 'Error! Message Code: KS723J', 'latepoint' );
520 $status = LATEPOINT_STATUS_ERROR;
521 }
522 } else {
523 $status = LATEPOINT_STATUS_ERROR;
524 $response_html = __( 'Error! Passwords do not match.', 'latepoint' );
525 }
526 } else {
527 $status = LATEPOINT_STATUS_ERROR;
528 $response_html = __( 'Customer Not Found', 'latepoint' );
529 }
530
531
532 if ( $this->get_return_format() == 'json' ) {
533 $this->send_json(
534 array(
535 'status' => $status,
536 'message' => $response_html,
537 )
538 );
539 }
540 }
541
542 public function set_account_password_on_booking_completion() {
543 $customer = OsAuthHelper::get_logged_in_customer();
544
545 if ( $customer ) {
546 $params = OsParamsHelper::permit_params(
547 $this->params,
548 [
549 'password',
550 'password_nonce',
551 ]
552 );
553 $this->check_nonce( 'set_initial_password_for_customer_' . $customer->get_uuid(), $params['password_nonce'] );
554 if ( ! empty( $params['password'] ) ) {
555 if ( $customer->update_password( $params['password'] ) ) {
556 $status = LATEPOINT_STATUS_SUCCESS;
557 $response_html = __( 'Account Password Set', 'latepoint' );
558 } else {
559 $response_html = __( 'Error! Message Code: KS723J', 'latepoint' );
560 $status = LATEPOINT_STATUS_ERROR;
561 }
562 } else {
563 $status = LATEPOINT_STATUS_ERROR;
564 $response_html = __( 'Error! Password is empty.', 'latepoint' );
565 }
566 } else {
567 $response_html = __( 'Invalid request', 'latepoint' );
568 $status = LATEPOINT_STATUS_ERROR;
569 }
570
571
572 if ( $this->get_return_format() == 'json' ) {
573 $this->send_json(
574 array(
575 'status' => $status,
576 'message' => $response_html,
577 )
578 );
579 }
580 }
581 }
582
583
584 endif;
585