PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / trunk
LatePoint – Calendar Booking Plugin for Appointments and Events vtrunk
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 2 months ago auth_controller.php 3 months ago booking_form_settings_controller.php 3 months ago bookings_controller.php 4 days ago calendars_controller.php 3 months ago carts_controller.php 4 days ago controller.php 3 months ago customer_cabinet_controller.php 2 months ago customers_controller.php 4 days ago dashboard_controller.php 2 months ago default_agent_controller.php 3 months ago events_controller.php 3 months ago form_fields_controller.php 1 week ago integrations_controller.php 3 months ago invoices_controller.php 4 days 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 4 days ago pro_controller.php 3 weeks ago process_jobs_controller.php 3 months ago processes_controller.php 1 month ago razorpay_connect_controller.php 1 week ago search_controller.php 3 months ago services_controller.php 3 months ago settings_controller.php 2 months ago steps_controller.php 3 weeks ago stripe_connect_controller.php 1 week ago support_topics_controller.php 3 months ago todos_controller.php 3 months ago transactions_controller.php 4 days ago wizard_controller.php 1 week ago
customer_cabinet_controller.php
590 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 $this->check_nonce( 'cancel_booking_' . $booking_id );
281 $booking = new OsBookingModel( $booking_id );
282 if ( ! empty( $booking->id ) && ( OsAuthHelper::get_logged_in_customer_id() == $booking->customer_id ) && OsCustomerHelper::can_cancel_booking( $booking ) ) {
283 if ( $booking->update_status( LATEPOINT_BOOKING_STATUS_CANCELLED ) ) {
284 $status = LATEPOINT_STATUS_SUCCESS;
285 $response_html = __( 'Appointment Status Updated', 'latepoint' );
286 } else {
287 $status = LATEPOINT_STATUS_ERROR;
288 $response_html = __( 'Error Updating Booking Status!', 'latepoint' ) . ' ' . implode( ',', $booking->get_error_messages() );
289 }
290 } else {
291 $status = LATEPOINT_STATUS_ERROR;
292 $response_html = __( 'Not allowed to cancel', 'latepoint' );
293 }
294 if ( $this->get_return_format() == 'json' ) {
295 $this->send_json(
296 array(
297 'status' => $status,
298 'message' => $response_html,
299 )
300 );
301 }
302 }
303
304 /*
305 Update profile
306 */
307
308 public function update() {
309 $customer = OsAuthHelper::get_logged_in_customer();
310 if ( ! $customer ) {
311 exit();
312 }
313 $this->check_nonce( 'update_customer_' . $customer->get_uuid() );
314
315
316 if ( $customer ) {
317 $old_customer_data = $customer->get_data_vars();
318 $customer->set_data( $this->params['customer'], LATEPOINT_PARAMS_SCOPE_CUSTOMER );
319 if ( $customer->save() ) {
320 $response_html = __( 'Information Saved', 'latepoint' );
321 $status = LATEPOINT_STATUS_SUCCESS;
322 do_action( 'latepoint_customer_updated', $customer, $old_customer_data );
323 } else {
324 $response_html = $customer->get_error_messages();
325 $status = LATEPOINT_STATUS_ERROR;
326 }
327 } else {
328 $response_html = __( 'Customer not found', 'latepoint' );
329 $status = LATEPOINT_STATUS_ERROR;
330 }
331 if ( $this->get_return_format() == 'json' ) {
332 $this->send_json(
333 array(
334 'status' => $status,
335 'message' => $response_html,
336 )
337 );
338 }
339 }
340
341 public function reload_booking_tile() {
342 if ( ! filter_var( $this->params['booking_id'], FILTER_VALIDATE_INT ) ) {
343 exit();
344 }
345
346 $booking_id = $this->params['booking_id'];
347 $booking = new OsBookingModel( $booking_id );
348
349 if ( $booking->id && OsAuthHelper::get_logged_in_customer_id() == $booking->customer_id ) {
350 $this->vars['booking'] = $booking;
351 $this->vars['is_upcoming_booking'] = $booking->is_upcoming();
352 $this->set_layout( 'none' );
353 $response_html = $this->format_render_return( '_booking_tile' );
354 $status = LATEPOINT_STATUS_SUCCESS;
355 } else {
356 $response_html = __( 'Invalid Booking', 'latepoint' );
357 $status = LATEPOINT_STATUS_ERROR;
358 }
359
360 if ( $this->get_return_format() == 'json' ) {
361 $this->send_json(
362 array(
363 'status' => $status,
364 'message' => $response_html,
365 )
366 );
367 }
368 }
369
370 public function logout() {
371 OsAuthHelper::logout_customer();
372 nocache_headers();
373 wp_redirect( OsSettingsHelper::get_customer_dashboard_url(), 302 );
374 }
375
376 public function login() {
377 $this->set_layout( 'none' );
378
379 return $this->format_render_return( __FUNCTION__ );
380 }
381
382 public function do_login() {
383 $customer = OsAuthHelper::login_customer( sanitize_email( $this->params['auth']['email'] ), $this->params['auth']['password'] );
384 if ( $customer ) {
385 $response_html = OsSettingsHelper::get_customer_dashboard_url();
386 $status = LATEPOINT_STATUS_SUCCESS;
387 } else {
388 $status = LATEPOINT_STATUS_ERROR;
389 $response_html = __( 'Invalid password or email', 'latepoint' );
390 }
391 if ( $this->get_return_format() == 'json' ) {
392 $this->send_json(
393 array(
394 'status' => $status,
395 'message' => $response_html,
396 )
397 );
398 }
399 }
400
401
402 public function password_reset_form() {
403 $this->vars['from_booking'] = ( isset( $this->params['from_booking'] ) && $this->params['from_booking'] );
404 $this->set_layout( 'none' );
405
406 return $this->format_render_return( __FUNCTION__ );
407 }
408
409 public function request_password_reset_token() {
410 $this->set_layout( 'none' );
411 $this->vars['from_booking'] = ( isset( $this->params['from_booking'] ) && $this->params['from_booking'] );
412
413 if ( isset( $this->params['password_reset_email'] ) ) {
414 $customer_model = new OsCustomerModel();
415 $customer = $customer_model->where( [ 'email' => sanitize_email( $this->params['password_reset_email'] ) ] )->set_limit( 1 )->get_results_as_models();
416 $customer_mailer = new OsCustomerMailer();
417
418 if ( $customer ) {
419 $customer->account_nonse = sha1( wp_rand( 10000, 99999 ) . time() . wp_generate_password( 32, true, true ) );
420 $customer->save();
421 }
422
423 if ( $customer && $customer_mailer->password_reset_request( $customer, $customer->account_nonse ) ) {
424 // Save timestamp for token expiration tracking (Unix timestamp for consistency)
425 OsMetaHelper::save_customer_meta_by_key( 'password_reset_token_created_at', current_time( 'timestamp' ), $customer->id );
426 return $this->format_render_return( 'password_reset_form' );
427 } else {
428 $this->vars['reset_token_error'] = ( $customer ) ? __( 'Error! Email was not sent.', 'latepoint' ) : __( 'Email does not match any customer', 'latepoint' );
429
430 return $this->format_render_return( __FUNCTION__ );
431 }
432 } else {
433 return $this->format_render_return( __FUNCTION__ );
434 }
435 }
436
437 public function dashboard( array $params = [] ) {
438 if ( ! OsAuthHelper::is_customer_logged_in() ) {
439 $this->set_layout( 'none' );
440
441 return $this->format_render_return( 'login' );
442 } else {
443 $customer = OsAuthHelper::get_logged_in_customer();
444 $this->vars['customer'] = $customer;
445 $this->vars['orders'] = $customer->get_orders();
446
447 $this->vars['future_bookings'] = $customer->get_future_bookings();
448 $this->vars['past_bookings'] = $customer->get_past_bookings();
449 $this->vars['cancelled_bookings'] = $customer->get_cancelled_bookings();
450 $this->vars['not_scheduled_bundles'] = $customer->get_not_scheduled_bundles();
451
452 $this->vars['cart_not_empty'] = ( ! OsCartsHelper::is_current_cart_empty() && OsCartsHelper::can_checkout_multiple_items() );
453
454 $this->vars['hide_new_appointment_ui'] = $params['hide_new_appointment_ui'] ?? false;
455
456 $this->set_layout( 'none' );
457
458 return $this->format_render_return( __FUNCTION__ );
459 }
460 }
461
462 public function change_password() {
463 $params = OsParamsHelper::permit_params(
464 $this->params,
465 [
466 'password_reset_token',
467 'password',
468 'password_confirmation',
469 'change_password_nonce',
470 ]
471 );
472
473 if ( empty( $params['password'] ) ) {
474 $this->send_json(
475 array(
476 'status' => LATEPOINT_STATUS_ERROR,
477 'message' => __( 'Password can not be blank', 'latepoint' ),
478 )
479 );
480 }
481
482 $status = LATEPOINT_STATUS_ERROR;
483 $response_html = __( 'Unknown Error', 'latepoint' );
484 $customer = false;
485
486 if ( OsAuthHelper::is_customer_logged_in() ) {
487 $this->check_nonce( 'change_password_' . OsAuthHelper::get_logged_in_customer_uuid(), $params['change_password_nonce'] );
488 $customer = OsAuthHelper::get_logged_in_customer();
489 } elseif ( ! empty( $params['password_reset_token'] ) ) {
490 $params['password_reset_token'] = sanitize_text_field( $params['password_reset_token'] );
491 $customer = OsCustomerHelper::get_by_account_nonse( $params['password_reset_token'] );
492
493 // Validate token expiration (15 minutes)
494 if ( $customer ) {
495 $created_at = OsMetaHelper::get_customer_meta_by_key( 'password_reset_token_created_at', $customer->id );
496
497 if ( empty( $created_at ) ) {
498 // No timestamp - reject for security (token created before fix)
499 $customer = false;
500 $status = LATEPOINT_STATUS_ERROR;
501 $response_html = __( 'Password token is invalid. Please request a new password reset.', 'latepoint' );
502 } else {
503 $age_seconds = current_time( 'timestamp' ) - intval( $created_at );
504 if ( $age_seconds > 900 ) { // 15 minutes = 900 seconds
505 // Clean up expired meta
506 OsMetaHelper::delete_customer_meta_by_key( 'password_reset_token_created_at', $customer->id );
507 $customer = false;
508 $status = LATEPOINT_STATUS_ERROR;
509 $response_html = __( 'Password token has expired. Please request a new password reset.', 'latepoint' );
510 }
511 }
512 }
513 }
514
515 if ( $customer ) {
516 if ( ! empty( $params['password'] ) && $params['password'] == $params['password_confirmation'] ) {
517 if ( $customer->update_password( $params['password'] ) ) {
518 // Invalidate token after successful password reset
519 if ( ! empty( $params['password_reset_token'] ) ) {
520 OsCustomerHelper::invalidate_password_reset_token( $customer );
521 }
522 $status = LATEPOINT_STATUS_SUCCESS;
523 $response_html = __( 'Your password was successfully updated.', 'latepoint' );
524 } else {
525 $response_html = __( 'Error! Message Code: KS723J', 'latepoint' );
526 $status = LATEPOINT_STATUS_ERROR;
527 }
528 } else {
529 $response_html = __( 'Error! Passwords do not match.', 'latepoint' );
530 $status = LATEPOINT_STATUS_ERROR;
531 }
532 } else {
533 $status = LATEPOINT_STATUS_ERROR;
534 $response_html = __( 'Customer Not Found', 'latepoint' );
535 }
536
537 if ( $this->get_return_format() == 'json' ) {
538 $this->send_json(
539 array(
540 'status' => $status,
541 'message' => $response_html,
542 )
543 );
544 }
545 }
546
547 public function set_account_password_on_booking_completion() {
548 $customer = OsAuthHelper::get_logged_in_customer();
549
550 if ( $customer ) {
551 $params = OsParamsHelper::permit_params(
552 $this->params,
553 [
554 'password',
555 'password_nonce',
556 ]
557 );
558 $this->check_nonce( 'set_initial_password_for_customer_' . $customer->get_uuid(), $params['password_nonce'] );
559 if ( ! empty( $params['password'] ) ) {
560 if ( $customer->update_password( $params['password'] ) ) {
561 $status = LATEPOINT_STATUS_SUCCESS;
562 $response_html = __( 'Account Password Set', 'latepoint' );
563 } else {
564 $response_html = __( 'Error! Message Code: KS723J', 'latepoint' );
565 $status = LATEPOINT_STATUS_ERROR;
566 }
567 } else {
568 $status = LATEPOINT_STATUS_ERROR;
569 $response_html = __( 'Error! Password is empty.', 'latepoint' );
570 }
571 } else {
572 $response_html = __( 'Invalid request', 'latepoint' );
573 $status = LATEPOINT_STATUS_ERROR;
574 }
575
576
577 if ( $this->get_return_format() == 'json' ) {
578 $this->send_json(
579 array(
580 'status' => $status,
581 'message' => $response_html,
582 )
583 );
584 }
585 }
586 }
587
588
589 endif;
590