PluginProbe
Bookit — Booking & Appointment Calendar / 2.6.0.5
Bookit — Booking & Appointment Calendar v2.6.0.5
2.6.0.5 2.6.0.4 2.6.0.3 2.6.0.2 2.6.0.1 2.6.0 trunk 1.2 1.2.2 1.2.3 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 All 62 releases
bookit / includes / classes / CustomerController.php

CustomerController.php in Bookit — Booking & Appointment Calendar 2.6.0.5, at includes/classes/CustomerController.php

239 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Bookit\Classes;
4
5 use Bookit\Classes\Base\User;
6 use Bookit\Classes\Database\Customers;
7
8 class CustomerController {
9
10 /**
11 * Appointment Customer
12 *
13 * @since 2.6.0 Resolve the customer from the authenticated session instead of request-supplied credentials.
14 * @since 2.6.0.2 Resolve an existing customer from the session identity.
15 * @since 2.6.0.2 Reuse an unlinked customer when the submitted contact fully matches.
16 *
17 * @param array $data
18 *
19 * @return object customer
20 */
21 public static function get_customer( $data ) {
22 $id = null;
23
24 if ( ! empty( $data['user_id'] ) ) {
25 $customer = Customers::get( 'wp_user_id', $data['user_id'] );
26 $id = $customer ? $customer->id : null;
27 }
28
29 if ( ! $id ) {
30 $customer = self::match_guest_customer( $data );
31 $id = $customer ? $customer->id : null;
32 }
33
34 if ( ! $id ) {
35 $id = self::save( $data );
36 }
37
38 self::maybe_update( $id, array(
39 'wp_user_id' => $data['user_id'],
40 'phone' => $data['phone'],
41 ) );
42
43 return Customers::get( 'id', $id );
44 }
45
46 /**
47 * Authenticate a returning customer through WordPress and return a
48 * session-ready payload so booking can continue without leaving the page.
49 *
50 * @since 2.6.0
51 */
52 public static function login() {
53 check_ajax_referer( 'bookit_login', 'nonce' );
54
55 $creds = array(
56 'user_login' => sanitize_text_field( wp_unslash( $_POST['email'] ?? '' ) ),
57 'user_password' => (string) ( $_POST['password'] ?? '' ),
58 'remember' => true,
59 );
60
61 // Make the just-issued session available to wp_create_nonce() in this request.
62 add_action( 'set_logged_in_cookie', array( self::class, 'sync_logged_in_cookie' ) );
63
64 $user = wp_signon( $creds );
65
66 if ( is_wp_error( $user ) ) {
67 // Keep credential failures generic so the form can't reveal which emails exist.
68 $generic = array( 'invalid_username', 'invalid_email', 'incorrect_password' );
69 $message = array_intersect( $generic, $user->get_error_codes() )
70 ? __( 'The email or password you entered is incorrect.', 'bookit' )
71 : wp_strip_all_tags( $user->get_error_message() );
72 wp_send_json_error( array( 'message' => $message ) );
73 }
74
75 wp_set_current_user( $user->ID );
76
77 wp_send_json_success( self::auth_payload( $user ) );
78 }
79
80 /**
81 * Register a new customer account, sign them in, and return a session-ready
82 * payload so booking can continue without leaving the page.
83 *
84 * @since 2.6.0
85 */
86 public static function register() {
87 check_ajax_referer( 'bookit_register', 'nonce' );
88
89 $email = sanitize_email( wp_unslash( $_POST['email'] ?? '' ) );
90 $full_name = sanitize_text_field( wp_unslash( $_POST['full_name'] ?? '' ) );
91 $password = (string) ( $_POST['password'] ?? '' );
92 $confirm = (string) ( $_POST['password_confirmation'] ?? '' );
93
94 $errors = array();
95 if ( ! is_email( $email ) ) {
96 $errors['email'] = __( 'Please enter your email in format youremail@example.com', 'bookit' );
97 }
98 if ( strlen( $full_name ) < 3 || strlen( $full_name ) > 25 ) {
99 $errors['full_name'] = __( 'Full name must be between 3 and 25 characters long', 'bookit' );
100 }
101 if ( empty( $password ) ) {
102 $errors['password'] = __( 'Please enter a password', 'bookit' );
103 } elseif ( false !== strpos( $password, '\\' ) ) {
104 $errors['password'] = __( "Passwords may not contain the character '\\'", 'bookit' );
105 } elseif ( $password !== $confirm ) {
106 $errors['password_confirmation'] = __( 'Please enter the same password in both password fields', 'bookit' );
107 }
108 if ( $errors ) {
109 wp_send_json_error( array( 'errors' => $errors ) );
110 }
111
112 if ( get_user_by( 'email', $email ) ) {
113 wp_send_json_error( array( 'message' => __( 'An account with this email already exists. Please log in.', 'bookit' ) ) );
114 }
115
116 $user_id = Customers::save_or_get_wp_user( array(
117 'email' => $email,
118 'password' => $password,
119 'full_name' => $full_name,
120 'role' => User::$customer_role,
121 ) );
122
123 // Never create or authenticate a privileged account through the booking form.
124 if ( is_wp_error( $user_id ) || ! $user_id || user_can( $user_id, 'administrator' ) ) {
125 wp_send_json_error( array( 'message' => __( 'Could not create your account. Please try again.', 'bookit' ) ) );
126 }
127
128 // Make the just-issued session available to wp_create_nonce() in this request.
129 add_action( 'set_logged_in_cookie', array( self::class, 'sync_logged_in_cookie' ) );
130
131 wp_clear_auth_cookie();
132 wp_set_current_user( $user_id );
133 wp_set_auth_cookie( $user_id );
134
135 wp_send_json_success( self::auth_payload( get_user_by( 'id', $user_id ) ) );
136 }
137
138 /**
139 * Expose the freshly-set login cookie to the rest of this request so nonces
140 * are minted against the new session token (they are verified on the next
141 * request, which carries that same cookie).
142 *
143 * @since 2.6.0
144 *
145 * @param string $logged_in_cookie
146 */
147 public static function sync_logged_in_cookie( $logged_in_cookie ) {
148 $_COOKIE[ LOGGED_IN_COOKIE ] = $logged_in_cookie;
149 }
150
151 /**
152 * Session-ready payload shared by login()/register(): the current user plus
153 * freshly minted nonces for the authenticated session.
154 *
155 * @since 2.6.0
156 *
157 * @param \WP_User $user
158 *
159 * @return array
160 */
161 private static function auth_payload( $user ) {
162 return array(
163 'user' => array(
164 'ID' => $user->ID,
165 'display_name' => $user->display_name,
166 'user_email' => $user->user_email,
167 'customer' => Customers::get( 'wp_user_id', $user->ID ),
168 'nonce' => wp_create_nonce( 'bookit_book_appointment' ),
169 ),
170 'nonces' => Nonces::get_frontend_nonces(),
171 );
172 }
173
174 /**
175 * Reuse an existing unlinked customer only when every submitted contact
176 * field matches, so repeat guest bookings do not create duplicate records.
177 *
178 * @since 2.6.0.2
179 *
180 * @param array $data Cleaned booking request data.
181 *
182 * @return object|null
183 */
184 private static function match_guest_customer( $data ) {
185 // Only anonymous bookings are matched; a session always resolves to its own customer.
186 if ( ! empty( $data['user_id'] ) ) {
187 return null;
188 }
189
190 if ( empty( $data['full_name'] ) || empty( $data['email'] ) || empty( $data['phone'] ) ) {
191 return null;
192 }
193
194 return Customers::get_by_contact( $data['full_name'], $data['email'], $data['phone'] );
195 }
196
197 /**
198 * Save Customer
199 *
200 * @param array $data
201 *
202 * @return int
203 */
204 private static function save( $data ) {
205 $insert = array(
206 'full_name' => $data['full_name'],
207 'wp_user_id' => $data['user_id'],
208 'email' => $data['email'],
209 'phone' => $data['phone'],
210 );
211 Customers::insert( $insert );
212
213 return Customers::insert_id();
214 }
215
216 /**
217 * Update Customer if appear new data
218 *
219 * @param int $id
220 * @param array $data
221 *
222 * @return void
223 */
224 private static function maybe_update( $id, $data ) {
225 $customer = Customers::get( 'id', $id );
226
227 if ( $customer->wp_user_id || $customer->wp_user_id == $data['wp_user_id'] ) {
228 unset( $data['wp_user_id'] );
229 }
230 if ( $customer->phone ) {
231 unset( $data['phone'] );
232 }
233
234 if ( $data ) {
235 Customers::update( $data, array( 'id' => $id ) );
236 }
237 }
238 }
239