PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.2.7
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.2.7
5.6.9 5.6.8 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 / customers_controller.php
latepoint / lib / controllers Last commit date
activities_controller.php 5 months ago auth_controller.php 9 months ago booking_form_settings_controller.php 1 year ago bookings_controller.php 1 year ago calendars_controller.php 9 months ago carts_controller.php 1 year ago controller.php 9 months ago customer_cabinet_controller.php 9 months ago customers_controller.php 5 months ago dashboard_controller.php 9 months ago default_agent_controller.php 1 year ago events_controller.php 1 year ago form_fields_controller.php 9 months ago integrations_controller.php 9 months ago invoices_controller.php 1 year ago manage_booking_by_key_controller.php 1 year ago manage_order_by_key_controller.php 1 year ago notifications_controller.php 1 year ago orders_controller.php 5 months ago pro_controller.php 1 year ago process_jobs_controller.php 9 months ago processes_controller.php 1 year ago search_controller.php 1 year ago services_controller.php 9 months ago settings_controller.php 5 months ago steps_controller.php 9 months ago stripe_connect_controller.php 9 months ago support_topics_controller.php 1 year ago todos_controller.php 1 year ago transactions_controller.php 1 year ago wizard_controller.php 5 months ago
customers_controller.php
492 lines
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly.
4 }
5
6
7 if ( ! class_exists( 'OsCustomersController' ) ) :
8
9
10 class OsCustomersController extends OsController {
11
12 function __construct() {
13 parent::__construct();
14
15
16 $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'customers/';
17 $this->vars['page_header'] = OsMenuHelper::get_menu_items_by_id( 'customers' );
18 $this->vars['breadcrumbs'][] = array( 'label' => __( 'Customers', 'latepoint' ), 'link' => OsRouterHelper::build_link( OsRouterHelper::build_route_name( 'customers', 'index' ) ) );
19 }
20
21 public function destroy() {
22 if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
23 $this->check_nonce( 'destroy_customer_' . $this->params['id'] );
24 $customer = new OsCustomerModel( $this->params['id'] );
25 if ( $customer->delete() ) {
26 $status = LATEPOINT_STATUS_SUCCESS;
27 $response_html = __( 'Customer Removed', 'latepoint' );
28 } else {
29 $status = LATEPOINT_STATUS_ERROR;
30 $response_html = __( 'Error Removing Customer', 'latepoint' );
31 }
32 } else {
33 $status = LATEPOINT_STATUS_ERROR;
34 $response_html = __( 'Error Removing Customer', 'latepoint' );
35 }
36
37 if ( $this->get_return_format() == 'json' ) {
38 $this->send_json( array( 'status' => $status, 'message' => $response_html ) );
39 }
40 }
41
42
43 public function view_customer_log() {
44
45 $activities = new OsActivityModel();
46 $activities = $activities->where( [ 'customer_id' => absint( $this->params['customer_id'] ) ] )->order_by( 'id desc' )->get_results_as_models();
47
48 $customer = new OsCustomerModel( $this->params['customer_id'] );
49
50 $this->vars['customer'] = $customer;
51 $this->vars['activities'] = $activities;
52
53 $this->format_render( __FUNCTION__ );
54 }
55
56
57 public function quick_new() {
58 $customer = new OsCustomerModel();
59
60 $this->vars['customer'] = $customer;
61
62 $this->format_render( 'quick_edit' );
63 }
64
65 public function quick_edit() {
66 if ( ! filter_var( $this->params['customer_id'], FILTER_VALIDATE_INT ) ) {
67 $this->access_not_allowed();
68 }
69 $customer = new OsCustomerModel( $this->params['customer_id'] );
70
71 $this->vars['customer'] = $customer;
72
73 $this->format_render( __FUNCTION__ );
74 }
75
76
77 public function inline_edit_form() {
78 $selected_customer = new OsCustomerModel();
79 if ( isset( $this->params['customer_id'] ) ) {
80 $selected_customer->load_by_id( $this->params['customer_id'] );
81 }
82 $this->vars['default_fields_for_customer'] = OsSettingsHelper::get_default_fields_for_customer();
83 $this->vars['selected_customer'] = $selected_customer;
84 $this->format_render( __FUNCTION__ );
85 }
86
87 public function set_as_guest() {
88 if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
89 $customer = new OsCustomerModel( $this->params['id'] );
90 if ( $customer->update_attributes( [ 'is_guest' => true ] ) ) {
91 $status = LATEPOINT_STATUS_SUCCESS;
92 $response_html = __( 'Customer is now allowed to book without password', 'latepoint' );
93 } else {
94 $status = LATEPOINT_STATUS_ERROR;
95 $response_html = $customer->get_error_messages();
96 }
97 } else {
98 $status = LATEPOINT_STATUS_ERROR;
99 $response_html = __( 'Error setting customer as guest', 'latepoint' );
100 }
101
102 if ( $this->get_return_format() == 'json' ) {
103 $this->send_json( array( 'status' => $status, 'message' => $response_html ) );
104 }
105 }
106
107 /*
108 Edit customer
109 */
110
111 public function edit_form() {
112 $this->vars['page_header'] = __( 'Edit Customer', 'latepoint' );
113 $this->vars['breadcrumbs'][] = array( 'label' => __( 'Edit Customer', 'latepoint' ), 'link' => false );
114
115 if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
116 // check if allowed to access
117 $customer = new OsCustomerModel();
118 $customer = $customer->where( [ LATEPOINT_TABLE_CUSTOMERS . '.id' => absint( $this->params['id'] ) ] )->filter_allowed_records()->set_limit( 1 )->get_results_as_models();
119 $this->vars['customer'] = $customer;
120 $this->vars['wp_users_for_select'] = OsWpUserHelper::get_wp_users_for_select();
121 }
122
123 $this->format_render( __FUNCTION__ );
124 }
125
126
127 public function query_for_booking_form() {
128 $query = trim( $this->params['query'] );
129 $sql_query = '%' . $query . '%';
130 $query = $this->params['query'];
131 $customers = new OsCustomerModel();
132 $this->vars['query'] = $query;
133 $this->vars['customers'] = $customers->where( array(
134 'OR' => array(
135 'CONCAT (first_name, " ", last_name) LIKE ' => $sql_query,
136 'email LIKE' => $sql_query,
137 'phone LIKE' => $sql_query
138 )
139 ) )->set_limit( 20 )->order_by( 'first_name asc, last_name asc' )->get_results_as_models();
140
141 $this->format_render( __FUNCTION__ );
142 }
143
144
145 /*
146 Create customer
147 */
148
149 public function create() {
150 $this->check_nonce( 'new_customer' );
151 $customer = new OsCustomerModel();
152 // Security fix: Prevent mass assignment of wordpress_user_id by non-admin users.
153 $customer->set_data( $this->params['customer'], LATEPOINT_PARAMS_SCOPE_PUBLIC );
154 if ( $customer->save() ) {
155 // translators: %s is the html of a customer edit link
156 $response_html = sprintf( __( 'Customer Created ID: %s', 'latepoint' ), '<span class="os-notification-link" ' . OsCustomerHelper::quick_customer_btn_html( $customer->id ) . '>' . $customer->id . '</span>' );
157 $status = LATEPOINT_STATUS_SUCCESS;
158 do_action( 'latepoint_customer_created', $customer );
159 } else {
160 $response_html = $customer->get_error_messages();
161 $status = LATEPOINT_STATUS_ERROR;
162 }
163 if ( $this->get_return_format() == 'json' ) {
164 $this->send_json( array( 'status' => $status, 'message' => $response_html ) );
165 }
166 }
167
168
169 /*
170 Update customer
171 */
172
173 public function update() {
174 if ( isset( $this->params['customer']['id'] ) && filter_var( $this->params['customer']['id'], FILTER_VALIDATE_INT ) ) {
175 $this->check_nonce( 'edit_customer_' . $this->params['customer']['id'] );
176 $customer = new OsCustomerModel( $this->params['customer']['id'] );
177 if ( ! $customer || ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'edit' ) ) {
178 $response_html = __( 'Access Restricted', 'latepoint' );
179 $status = LATEPOINT_STATUS_ERROR;
180 } else {
181 $old_customer_data = $customer->get_data_vars();
182 // Security fix: Prevent mass assignment of wordpress_user_id by non-admin users.
183 $customer->set_data( $this->params['customer'], LATEPOINT_PARAMS_SCOPE_PUBLIC );
184 if ( $customer->save() ) {
185 // translators: %s is the html of a customer edit link
186 $response_html = sprintf( __( 'Customer Updated ID: %s', 'latepoint' ), '<span class="os-notification-link" ' . OsCustomerHelper::quick_customer_btn_html( $customer->id ) . '>' . $customer->id . '</span>' );
187 $status = LATEPOINT_STATUS_SUCCESS;
188 do_action( 'latepoint_customer_updated', $customer, $old_customer_data );
189 } else {
190 $response_html = $customer->get_error_messages();
191 $status = LATEPOINT_STATUS_ERROR;
192 }
193 }
194 } else {
195 $response_html = __( 'Invalid customer ID', 'latepoint' );
196 $status = LATEPOINT_STATUS_ERROR;
197 }
198 if ( $this->get_return_format() == 'json' ) {
199 $this->send_json( array( 'status' => $status, 'message' => $response_html ) );
200 }
201 }
202
203 public function mini_profile() {
204 if ( filter_var( $this->params['customer_id'], FILTER_VALIDATE_INT ) ) {
205 $customer = new OsCustomerModel( $this->params['customer_id'] );
206 $this->vars['upcoming_booking'] = $customer->get_future_bookings( 1, true );
207 $this->vars['customer'] = $customer;
208
209
210 $pie_labels = [];
211 $pie_colors = [];
212 $pie_values = [];
213 $pie_chart_data = OsBookingHelper::get_stat( 'bookings', [ 'group_by' => 'status', 'customer_id' => $customer->id ] );
214 $colors = [ '#2752E4', '#C066F1', '#26B7DD', '#E8C634', '#19CED6', '#2FEAA3', '#252a3e', '#8d87a5', '#b9b784' ];
215 $status_colors = [
216 LATEPOINT_BOOKING_STATUS_APPROVED => '#35d893',
217 LATEPOINT_BOOKING_STATUS_PENDING => '#e6b935',
218 LATEPOINT_BOOKING_STATUS_PAYMENT_PENDING => '#4ca4ef',
219 LATEPOINT_BOOKING_STATUS_CANCELLED => '#f1585d'
220 ];
221 $i = 0;
222 foreach ( $pie_chart_data as $pie_data ) {
223 $pie_labels[] = $pie_data['status'];
224 $pie_colors[] = isset( $status_colors[ $pie_data['status'] ] ) ? $status_colors[ $pie_data['status'] ] : $colors[ $i ];
225 $pie_values[] = $pie_data['stat'];
226 $i ++;
227 }
228
229 $this->vars['pie_chart_data'] = [ 'labels' => $pie_labels, 'colors' => $pie_colors, 'values' => $pie_values ];
230
231
232 $this->set_layout( 'none' );
233 $response_html = $this->format_render_return( __FUNCTION__ );
234 } else {
235 $status = LATEPOINT_STATUS_ERROR;
236 $response_html = __( 'Error Accessing Customer', 'latepoint' );
237 }
238
239 if ( $this->get_return_format() == 'json' ) {
240 $this->send_json( array( 'status' => $status, 'message' => $response_html ) );
241 }
242 }
243
244 public function connect_all_to_wp_users() {
245 $customers = new OsCustomerModel();
246 $customers = $customers->where( [ 'wordpress_user_id' => [ 'OR' => [ 0, 'IS NULL' ] ] ] )->get_results_as_models();
247 if ( $customers ) {
248 foreach ( $customers as $customer ) {
249 $wp_user_id = OsCustomerHelper::create_wp_user_for_customer( $customer );
250 if ( $wp_user_id ) {
251 //check if wp user already connected to another customer
252 $connected_customer = new OsCustomerModel();
253 $connected_customer = $connected_customer->where( [ 'wordpress_user_id' => $wp_user_id ] )->set_limit( 1 )->get_results_as_models();
254 if ( ! $connected_customer ) {
255 $customer->update_attributes( [ 'wordpress_user_id' => $wp_user_id ] );
256 }
257 }
258 }
259 }
260
261 if ( $this->get_return_format() == 'json' ) {
262 $this->send_json( array( 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => __( 'Customers Connected', 'latepoint' ) ) );
263 }
264 }
265
266 public function disconnect_from_wp_user() {
267 $customer_id = $this->params['customer_id'];
268 $customer = new OsCustomerModel();
269 $customer = $customer->where( [ 'id' => $customer_id ] )->set_limit( 1 )->get_results_as_models();
270 if ( $customer ) {
271 $customer->update_attributes( [ 'wordpress_user_id' => null ] );
272 }
273 if ( $this->get_return_format() == 'json' ) {
274 $this->send_json( array( 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => __( 'Customer Disconnected', 'latepoint' ) ) );
275 }
276 }
277
278 public function connect_to_wp_user() {
279 $customer_id = $this->params['customer_id'];
280 $customer = new OsCustomerModel();
281 $customer = $customer->where( [ 'id' => $customer_id ] )->set_limit( 1 )->get_results_as_models();
282 if ( $customer && ! $customer->wordpress_user_id ) {
283 $wp_user = OsCustomerHelper::create_wp_user_for_customer( $customer );
284 }
285 if ( $this->get_return_format() == 'json' ) {
286 $this->send_json( array( 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => __( 'Customer Connected', 'latepoint' ) ) );
287 }
288 }
289
290
291 public function index() {
292
293
294 $this->vars['page_header'] = false;
295 $page_number = isset( $this->params['page_number'] ) ? $this->params['page_number'] : 1;
296 $per_page = OsSettingsHelper::get_number_of_records_per_page();
297 $offset = ( $page_number > 1 ) ? ( ( $page_number - 1 ) * $per_page ) : 0;
298
299
300 $customers = new OsCustomerModel();
301 $query_args = [];
302
303 $filter = isset( $this->params['filter'] ) ? $this->params['filter'] : false;
304
305 // TABLE SEARCH FILTERS
306 if ( $filter ) {
307 if ( $filter['id'] ) {
308 $query_args['id'] = $filter['id'];
309 }
310 if ( $filter['registration_date_from'] && $filter['registration_date_to'] ) {
311 $query_args[ LATEPOINT_TABLE_CUSTOMERS . '.created_at >=' ] = $filter['registration_date_from'];
312 $query_args[ LATEPOINT_TABLE_CUSTOMERS . '.created_at <=' ] = $filter['registration_date_to'];
313 }
314 if ( $filter['customer'] ) {
315 $query_args[ 'concat_ws(" ", ' . LATEPOINT_TABLE_CUSTOMERS . '.first_name,' . LATEPOINT_TABLE_CUSTOMERS . '.last_name) LIKE' ] = '%' . $filter['customer'] . '%';
316 $this->vars['customer_name_query'] = $filter['customer'];
317 }
318 if ( $filter['phone'] ) {
319 $query_args['phone LIKE'] = '%' . $filter['phone'] . '%';
320 $this->vars['phone_query'] = $filter['phone'];
321 }
322 if ( $filter['email'] ) {
323 $query_args['email LIKE'] = '%' . $filter['email'] . '%';
324 $this->vars['email_query'] = $filter['email'];
325 }
326 }
327
328
329 // OUTPUT CSV IF REQUESTED
330 if ( isset( $this->params['download'] ) && $this->params['download'] == 'csv' ) {
331 $csv_filename = 'customers_' . OsUtilHelper::random_text();
332
333 header( "Content-Type: text/csv" );
334 header( "Content-Disposition: attachment; filename={$csv_filename}.csv" );
335
336 $labels_row = [
337 __( 'ID', 'latepoint' ),
338 __( 'Name', 'latepoint' ),
339 __( 'Phone', 'latepoint' ),
340 __( 'Email', 'latepoint' ),
341 __( 'Total Appointments', 'latepoint' ),
342 __( 'Next Appointment', 'latepoint' ),
343 __( 'Registered On', 'latepoint' )
344 ];
345
346
347 $customers_data = [];
348 $customers_data[] = $labels_row;
349
350
351 $customers_arr = $customers->where( $query_args )->filter_allowed_records()->order_by( 'id desc' )->get_results_as_models();
352 if ( $customers_arr ) {
353 foreach ( $customers_arr as $customer ) {
354 $next_booking = $customer->get_future_bookings( 1, true );
355 $values_row = [
356 $customer->id,
357 $customer->full_name,
358 $customer->phone,
359 $customer->email,
360 $customer->total_bookings_count,
361 $next_booking ? $next_booking->nice_start_datetime : 'n/a',
362 $customer->formatted_created_date()
363 ];
364 $values_row = apply_filters( 'latepoint_customer_row_for_csv_export', $values_row, $customer, $this->params );
365 $customers_data[] = $values_row;
366 }
367 }
368 $customers_data = apply_filters( 'latepoint_customers_data_for_csv_export', $customers_data, $this->params );
369 OsCSVHelper::array_to_csv( $customers_data );
370
371 return;
372 }
373
374 $customers->where( $query_args )->filter_allowed_records();
375 $count_total_customers = clone $customers;
376
377 $total_customers = $count_total_customers->count();
378 $total_pages = ceil( $total_customers / $per_page );
379
380
381 $this->vars['customers_violating_auth_rules'] = OsAuthHelper::count_total_customers_violating_auth_rules();
382
383 $this->vars['customers'] = $customers->set_limit( $per_page )->set_offset( $offset )->order_by( 'id desc' )->get_results_as_models();
384 $this->vars['total_customers'] = $total_customers;
385
386 $this->vars['total_pages'] = ceil( $total_customers / $per_page );
387 $this->vars['per_page'] = $per_page;
388 $this->vars['current_page_number'] = $page_number;
389
390 $this->vars['showing_from'] = ( ( $page_number - 1 ) * $per_page ) ? ( ( $page_number - 1 ) * $per_page ) : 1;
391 $this->vars['showing_to'] = min( $page_number * $per_page, $this->vars['total_customers'] );
392
393 $this->format_render( [ 'json_view_name' => '_table_body', 'html_view_name' => __FUNCTION__ ], [], [
394 'total_pages' => $total_pages,
395 'showing_from' => $this->vars['showing_from'],
396 'showing_to' => $this->vars['showing_to'],
397 'total_records' => $total_customers
398 ] );
399 }
400
401
402 public function import_csv_modal() {
403 $current_step = $this->params['step'] ?? 'upload_csv';
404 $steps = [
405 'upload_csv' => [ 'next_step' => 'mapping' ],
406 'mapping' => [ 'next_step' => 'importing' ],
407 ];
408 $this->vars['current_step'] = $current_step;
409 $this->vars['next_step'] = array_key_exists( $current_step, $steps ) ? $steps[ $current_step ]['next_step'] : 'upload_csv';
410 $this->format_render( __FUNCTION__ );
411 }
412
413 public function import_load_step() {
414 $this->check_nonce( 'import_customers_csv' );
415 $current_step = $this->params['step'] ?? 'upload_csv';
416 $status = LATEPOINT_STATUS_SUCCESS;
417
418 try {
419 switch ( $current_step ) {
420 case 'upload_csv':
421 $response_html = $this->_handle_upload_step();
422 break;
423
424 case 'mapping':
425 $response_html = $this->_handle_mapping_step();
426 break;
427
428 case 'confirmation':
429 $response_html = $this->_handleConfirmationStep();
430 break;
431
432 default:
433 throw new Exception( 'Invalid step provided' );
434 }
435 } catch ( Exception $e ) {
436 $response_html = $e->getMessage();
437 $status = LATEPOINT_STATUS_ERROR;
438 }
439
440 $this->send_json( array( 'status' => $status, 'message' => $response_html ) );
441 }
442
443 private function _handle_upload_step(): string {
444 $file_path = OsCSVHelper::upload_csv_file( $this->files, 'latepoint_customers_csv' );
445 $csv_data = OsCSVHelper::get_csv_data( $file_path, 1 );
446
447 return $this->render( $this->views_folder . 'import_steps/step_mapping.php', 'none', [
448 'csv_data' => $csv_data,
449 ] );
450 }
451
452 private function _handle_mapping_step(): string {
453 $columnMapping = $this->params['latepoint_column_mapping'] ?? [];
454
455 if ( ! OsCustomerImportHelper::validate_import_mapping( $columnMapping ) ) {
456 throw new Exception( esc_html__( 'Email field is required', 'latepoint' ) );
457 }
458
459 $csv_data = OsCSVHelper::get_csv_data( OsCustomerImportHelper::get_import_tmp_filepath() );
460 $validation_result = OsCustomerImportHelper::validate_csv_data( $csv_data, $columnMapping );
461
462 return $this->render( $this->views_folder . 'import_steps/step_confirmation.php', 'none', [
463 'conflicts' => $validation_result['conflicts'],
464 'can_be_imported' => $validation_result['importable_count'],
465 'latepoint_column_mapping' => $columnMapping
466 ] );
467 }
468
469
470 private function _handleConfirmationStep(): string {
471 $update_existing_customers = ! empty( $this->params['latepoint_update_customers_acknowledgement'] ) && OsUtilHelper::is_on( $this->params['latepoint_update_customers_acknowledgement'] );
472 $column_mapping = ! empty( $this->params['latepoint_column_mapping'] ) ? json_decode( $this->params['latepoint_column_mapping'], true ) : [];
473
474 $file_path = OsCustomerImportHelper::get_import_tmp_filepath();
475 $csv_data = OsCSVHelper::get_csv_data( $file_path );
476
477 $importResult = OsCustomerImportHelper::import_customers( $csv_data, $column_mapping, $update_existing_customers );
478
479 // Cleanup after successful import
480 OsCustomerImportHelper::cleanup_stored_file();
481
482 return $this->render( $this->views_folder . 'import_steps/step_done.php', 'none', [
483 'skipped_records' => $importResult['skipped_count'],
484 'updated_records' => $importResult['updated_count'],
485 ] );
486 }
487
488
489 }
490
491
492 endif;