PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.5.0
Forumax – AI Powered Advanced Community Forum Plugin v2.5.0
2.5.0 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 2.0.0 All 30 releases
bbp-core / includes / login-form.php

login-form.php in Forumax – AI Powered Advanced Community Forum Plugin 2.5.0, at includes/login-form.php

321 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Forumax Login & Registration Form Shortcode
4 *
5 * Renders a tabbed Login / Sign Up form. Uses wp_login_form() to ensure
6 * Google Site Kit (and any plugin that hooks into `login_form`) works.
7 *
8 * @package Forumax
9 */
10
11 /**
12 * Render the tabbed login and registration form.
13 *
14 * @param array $atts Shortcode attributes.
15 *
16 * @return string HTML output.
17 */
18 function forumax_login_form_shortcode( $atts ) {
19 // If user already logged in, return a message.
20 if ( is_user_logged_in() ) {
21 return '<div class="frmx-auth-logged-in">' . esc_html__( 'You are already logged in.', 'forumax' ) . '</div>';
22 }
23
24 // Same shortcode is used on several spots of one page (reply prompt, topic
25 // prompt, subscribe modal, page content). Each instance needs its own IDs
26 // so a later form is not dropped and tabs/labels stay wired to that form.
27 static $frmx_login_form_instance = 0;
28 $frmx_login_form_instance++;
29 $uid = 'frmx-auth-' . $frmx_login_form_instance;
30
31 // Extract shortcode attributes with defaults.
32 $atts = shortcode_atts( array(
33 'redirect' => '',
34 ), $atts );
35
36 // Set redirect URL.
37 $redirect_url = ! empty( $atts['redirect'] ) ? $atts['redirect'] : get_permalink();
38
39 $can_register = get_option( 'users_can_register' );
40 $wrapper_id = $uid;
41
42 ob_start();
43 ?>
44 <div class="frmx-auth-tabs" id="<?php echo esc_attr( $wrapper_id ); ?>">
45
46 <?php if ( $can_register ) : ?>
47 <div class="frmx-auth-tabs-header">
48 <button type="button" class="frmx-auth-tab frmx-auth-tab-active" data-frmx-tab="login">
49 <?php esc_html_e( 'Sign In', 'forumax' ); ?>
50 </button>
51 <button type="button" class="frmx-auth-tab" data-frmx-tab="register">
52 <?php esc_html_e( 'Create Account', 'forumax' ); ?>
53 </button>
54 </div>
55 <?php endif; ?>
56
57 <!-- Login Tab -->
58 <div class="frmx-auth-panel frmx-auth-panel-active" data-frmx-panel="login">
59 <?php
60 $form_html = wp_login_form( array(
61 'redirect' => $redirect_url,
62 'label_username' => esc_html__( 'Username or Email', 'forumax' ),
63 'label_password' => esc_html__( 'Password', 'forumax' ),
64 'label_remember' => esc_html__( 'Remember Me', 'forumax' ),
65 'label_log_in' => esc_html__( 'Sign In', 'forumax' ),
66 'form_id' => $uid . '-login',
67 'id_username' => $uid . '-user',
68 'id_password' => $uid . '-pass',
69 'id_remember' => $uid . '-remember',
70 'id_submit' => $uid . '-submit',
71 'remember' => true,
72 'echo' => false,
73 ) );
74
75 // Class hook for styling (keeps a stable selector with form_id).
76 $form_html = preg_replace(
77 '/<form\b/i',
78 '<form class="forumax_login_form"',
79 $form_html,
80 1
81 );
82
83 // Add placeholders to core login form fields.
84 $username_placeholder = esc_attr__( 'Username or Email', 'forumax' );
85 $password_placeholder = esc_attr__( 'Password', 'forumax' );
86
87 $form_html = preg_replace(
88 '/(<input\b(?![^>]*\bplaceholder=)[^>]*\bname=["\']log["\'][^>]*)(\/?\s*>)/i',
89 '$1 placeholder="' . $username_placeholder . '"$2',
90 $form_html
91 );
92 $form_html = preg_replace(
93 '/(<input\b(?![^>]*\bplaceholder=)[^>]*\bname=["\']pwd["\'][^>]*)(\/?\s*>)/i',
94 '$1 placeholder="' . $password_placeholder . '"$2',
95 $form_html
96 );
97
98 echo $form_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
99 ?>
100
101 <?php
102 /**
103 * Hook to display social login buttons (e.g. Google Site Kit).
104 */
105 do_action( 'forumax_login_form_social_buttons' );
106 ?>
107 </div>
108
109 <?php if ( $can_register ) : ?>
110 <!-- Registration Tab -->
111 <div class="frmx-auth-panel" data-frmx-panel="register">
112 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="frmx-auth-register-form">
113 <input type="hidden" name="action" value="forumax_register" />
114 <input type="hidden" name="redirect_to" value="<?php echo esc_url( $redirect_url ); ?>" />
115
116 <div class="frmx-auth-field">
117 <label for="<?php echo esc_attr( $uid ); ?>-reg-login"><?php esc_html_e( 'Username', 'forumax' ); ?></label>
118 <input type="text" name="user_login" id="<?php echo esc_attr( $uid ); ?>-reg-login" placeholder="<?php esc_attr_e( 'Username', 'forumax' ); ?>" autocomplete="username" required />
119 </div>
120
121 <div class="frmx-auth-field">
122 <label for="<?php echo esc_attr( $uid ); ?>-reg-email"><?php esc_html_e( 'Email', 'forumax' ); ?></label>
123 <input type="email" name="user_email" id="<?php echo esc_attr( $uid ); ?>-reg-email" placeholder="<?php esc_attr_e( 'Email', 'forumax' ); ?>" autocomplete="email" required />
124 </div>
125
126 <div class="frmx-auth-field">
127 <label for="<?php echo esc_attr( $uid ); ?>-reg-pass"><?php esc_html_e( 'Password', 'forumax' ); ?></label>
128 <input type="password" name="user_pass" id="<?php echo esc_attr( $uid ); ?>-reg-pass" placeholder="<?php esc_attr_e( 'Password', 'forumax' ); ?>" autocomplete="new-password" required />
129 </div>
130
131 <div class="frmx-auth-field">
132 <label for="<?php echo esc_attr( $uid ); ?>-reg-pass-confirm"><?php esc_html_e( 'Confirm Password', 'forumax' ); ?></label>
133 <input type="password" name="user_pass_confirm" id="<?php echo esc_attr( $uid ); ?>-reg-pass-confirm" placeholder="<?php esc_attr_e( 'Confirm Password', 'forumax' ); ?>" autocomplete="new-password" required />
134 </div>
135
136 <?php
137 /**
138 * Fires inside the registration form.
139 *
140 * This is the standard WordPress hook that plugins (e.g. CAPTCHA,
141 * custom fields) use to inject content into registration forms.
142 */
143 do_action( 'register_form' );
144 ?>
145
146 <div class="frmx-auth-submit">
147 <button type="submit" id="<?php echo esc_attr( $uid ); ?>-register" name="wp-submit" class="button submit fill-brand">
148 <?php esc_html_e( 'Register', 'forumax' ); ?>
149 </button>
150 </div>
151
152 <?php wp_nonce_field( 'forumax_register', 'forumax_register_nonce' ); ?>
153 </form>
154 </div>
155 <?php endif; ?>
156
157 </div>
158
159 <script>
160 ( function() {
161 var wrapper = document.getElementById( '<?php echo esc_js( $wrapper_id ); ?>' );
162 if ( ! wrapper ) {
163 return;
164 }
165
166 var tabs = wrapper.querySelectorAll( '.frmx-auth-tab' );
167 var panels = wrapper.querySelectorAll( '.frmx-auth-panel' );
168
169 tabs.forEach( function( tab ) {
170 tab.addEventListener( 'click', function() {
171 var target = this.getAttribute( 'data-frmx-tab' );
172
173 tabs.forEach( function( t ) {
174 t.classList.remove( 'frmx-auth-tab-active' );
175 } );
176 this.classList.add( 'frmx-auth-tab-active' );
177
178 panels.forEach( function( p ) {
179 if ( p.getAttribute( 'data-frmx-panel' ) === target ) {
180 p.classList.add( 'frmx-auth-panel-active' );
181 } else {
182 p.classList.remove( 'frmx-auth-panel-active' );
183 }
184 } );
185 } );
186 } );
187
188 /* Password visibility toggle */
189 var eyeShow = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>';
190 var eyeHide = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>';
191
192 wrapper.querySelectorAll( 'input[type="password"]' ).forEach( function( input ) {
193 var container = document.createElement( 'div' );
194 container.className = 'frmx-auth-password-wrap';
195 input.parentNode.insertBefore( container, input );
196 container.appendChild( input );
197
198 var toggleBtn = document.createElement( 'button' );
199 toggleBtn.type = 'button';
200 toggleBtn.className = 'frmx-auth-eye-toggle';
201 toggleBtn.setAttribute( 'aria-label', 'Toggle password visibility' );
202 toggleBtn.innerHTML = eyeShow;
203 container.appendChild( toggleBtn );
204
205 toggleBtn.addEventListener( 'click', function() {
206 var isPassword = input.type === 'password';
207 input.type = isPassword ? 'text' : 'password';
208 this.innerHTML = isPassword ? eyeHide : eyeShow;
209 input.focus();
210 } );
211 } );
212 } )();
213 </script>
214 <?php
215
216 return ob_get_clean();
217 }
218
219 add_shortcode( 'forumax_login_form', 'forumax_login_form_shortcode' );
220
221 /**
222 * Redirect to the referrer page after a failed login attempt.
223 *
224 * @param string $username The username that failed login.
225 *
226 * @return void
227 */
228 function forumax_login_failed_redirect( $username ) {
229 $referrer = wp_get_referer();
230 if ( ! empty( $referrer ) && ! str_contains( $referrer, 'wp-login.php' ) ) {
231 wp_redirect( add_query_arg( 'login', 'failed', $referrer ) );
232 exit;
233 }
234 }
235
236 add_action( 'wp_login_failed', 'forumax_login_failed_redirect' );
237
238 /**
239 * Handle the custom registration form submission.
240 *
241 * Creates a new user with the provided username, email, and password,
242 * auto-logs them in, and redirects to the referring page.
243 *
244 * @return void
245 */
246 function forumax_handle_registration() {
247 // Verify nonce.
248 if ( ! isset( $_POST['forumax_register_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['forumax_register_nonce'] ) ), 'forumax_register' ) ) {
249 wp_die( esc_html__( 'Security check failed.', 'forumax' ), 403 );
250 }
251
252 // Check registration is allowed.
253 if ( ! get_option( 'users_can_register' ) ) {
254 wp_die( esc_html__( 'Registration is currently disabled.', 'forumax' ), 403 );
255 }
256
257 $redirect = ! empty( $_POST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_POST['redirect_to'] ) ) : home_url();
258 $referrer = wp_get_referer();
259 if ( empty( $referrer ) ) {
260 $referrer = $redirect;
261 }
262
263 $user_login = isset( $_POST['user_login'] ) ? sanitize_user( wp_unslash( $_POST['user_login'] ) ) : '';
264 $user_email = isset( $_POST['user_email'] ) ? sanitize_email( wp_unslash( $_POST['user_email'] ) ) : '';
265 $user_pass = isset( $_POST['user_pass'] ) ? $_POST['user_pass'] : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- password must not be sanitized.
266 $user_pass_confirm = isset( $_POST['user_pass_confirm'] ) ? $_POST['user_pass_confirm'] : ''; // phpcs:ignore
267
268 // Validate fields.
269 $errors = array();
270
271 if ( empty( $user_login ) ) {
272 $errors[] = 'empty_username';
273 }
274
275 if ( empty( $user_email ) || ! is_email( $user_email ) ) {
276 $errors[] = 'invalid_email';
277 }
278
279 if ( empty( $user_pass ) ) {
280 $errors[] = 'empty_password';
281 }
282
283 if ( $user_pass !== $user_pass_confirm ) {
284 $errors[] = 'password_mismatch';
285 }
286
287 if ( ! empty( $errors ) ) {
288 wp_safe_redirect( add_query_arg( 'reg_error', implode( ',', $errors ), $referrer ) );
289 exit;
290 }
291
292 // Let external plugins validate registration (e.g. Envato license check).
293 $sbv_valid = apply_filters( 'sbv_validate_registration', true );
294 if ( is_wp_error( $sbv_valid ) ) {
295 wp_safe_redirect( add_query_arg( 'reg_error', $sbv_valid->get_error_code(), $referrer ) );
296 exit;
297 }
298
299 // Create the user.
300 $user_id = wp_create_user( $user_login, $user_pass, $user_email );
301
302 if ( is_wp_error( $user_id ) ) {
303 $error_code = $user_id->get_error_code();
304 wp_safe_redirect( add_query_arg( 'reg_error', $error_code, $referrer ) );
305 exit;
306 }
307
308 // Fire standard WordPress registration action.
309 do_action( 'user_register', $user_id );
310
311 // Auto-login the new user.
312 wp_set_current_user( $user_id );
313 wp_set_auth_cookie( $user_id, true );
314
315 wp_safe_redirect( $redirect );
316 exit;
317 }
318
319 add_action( 'admin_post_nopriv_forumax_register', 'forumax_handle_registration' );
320 add_action( 'admin_post_forumax_register', 'forumax_handle_registration' );
321