PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.4.2
Forumax – AI Powered Advanced Community Forum Plugin v2.4.2
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 2.1.0 All 29 releases
bbp-core / includes / login-form.php

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

302 lines 10.6 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 // Extract shortcode attributes with defaults.
25 $atts = shortcode_atts( array(
26 'redirect' => '',
27 ), $atts );
28
29 // Set redirect URL.
30 $redirect_url = ! empty( $atts['redirect'] ) ? $atts['redirect'] : get_permalink();
31
32 $can_register = get_option( 'users_can_register' );
33 $wrapper_id = uniqid( 'frmx-auth-tabs-' );
34
35 ob_start();
36 ?>
37 <div class="frmx-auth-tabs" id="<?php echo esc_attr( $wrapper_id ); ?>">
38
39 <?php if ( $can_register ) : ?>
40 <div class="frmx-auth-tabs-header">
41 <button type="button" class="frmx-auth-tab frmx-auth-tab-active" data-frmx-tab="login">
42 <?php esc_html_e( 'Sign In', 'forumax' ); ?>
43 </button>
44 <button type="button" class="frmx-auth-tab" data-frmx-tab="register">
45 <?php esc_html_e( 'Create Account', 'forumax' ); ?>
46 </button>
47 </div>
48 <?php endif; ?>
49
50 <!-- Login Tab -->
51 <div class="frmx-auth-panel frmx-auth-panel-active" data-frmx-panel="login">
52 <?php
53 $form_html = wp_login_form( array(
54 'redirect' => $redirect_url,
55 'label_username' => esc_html__( 'Username or Email', 'forumax' ),
56 'label_password' => esc_html__( 'Password', 'forumax' ),
57 'label_remember' => esc_html__( 'Remember Me', 'forumax' ),
58 'label_log_in' => esc_html__( 'Sign In', 'forumax' ),
59 'form_id' => 'forumax_login_form',
60 'remember' => true,
61 'echo' => false,
62 ) );
63
64 // Add placeholders to core login form fields.
65 $username_placeholder = esc_attr__( 'Username or Email', 'forumax' );
66 $password_placeholder = esc_attr__( 'Password', 'forumax' );
67
68 $form_html = preg_replace(
69 '/(<input\b(?![^>]*\bplaceholder=)[^>]*\bname=["\']log["\'][^>]*)(\/?\s*>)/i',
70 '$1 placeholder="' . $username_placeholder . '"$2',
71 $form_html
72 );
73 $form_html = preg_replace(
74 '/(<input\b(?![^>]*\bplaceholder=)[^>]*\bname=["\']pwd["\'][^>]*)(\/?\s*>)/i',
75 '$1 placeholder="' . $password_placeholder . '"$2',
76 $form_html
77 );
78
79 echo $form_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
80 ?>
81
82 <?php
83 /**
84 * Hook to display social login buttons (e.g. Google Site Kit).
85 */
86 do_action( 'forumax_login_form_social_buttons' );
87 ?>
88 </div>
89
90 <?php if ( $can_register ) : ?>
91 <!-- Registration Tab -->
92 <div class="frmx-auth-panel" data-frmx-panel="register">
93 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="frmx-auth-register-form">
94 <input type="hidden" name="action" value="forumax_register" />
95 <input type="hidden" name="redirect_to" value="<?php echo esc_url( $redirect_url ); ?>" />
96
97 <div class="frmx-auth-field">
98 <label for="frmx_reg_user_login"><?php esc_html_e( 'Username', 'forumax' ); ?></label>
99 <input type="text" name="user_login" id="frmx_reg_user_login" placeholder="<?php esc_attr_e( 'Username', 'forumax' ); ?>" autocomplete="username" required />
100 </div>
101
102 <div class="frmx-auth-field">
103 <label for="frmx_reg_user_email"><?php esc_html_e( 'Email', 'forumax' ); ?></label>
104 <input type="email" name="user_email" id="frmx_reg_user_email" placeholder="<?php esc_attr_e( 'Email', 'forumax' ); ?>" autocomplete="email" required />
105 </div>
106
107 <div class="frmx-auth-field">
108 <label for="frmx_reg_user_pass"><?php esc_html_e( 'Password', 'forumax' ); ?></label>
109 <input type="password" name="user_pass" id="frmx_reg_user_pass" placeholder="<?php esc_attr_e( 'Password', 'forumax' ); ?>" autocomplete="new-password" required />
110 </div>
111
112 <div class="frmx-auth-field">
113 <label for="frmx_reg_user_pass_confirm"><?php esc_html_e( 'Confirm Password', 'forumax' ); ?></label>
114 <input type="password" name="user_pass_confirm" id="frmx_reg_user_pass_confirm" placeholder="<?php esc_attr_e( 'Confirm Password', 'forumax' ); ?>" autocomplete="new-password" required />
115 </div>
116
117 <?php
118 /**
119 * Fires inside the registration form.
120 *
121 * This is the standard WordPress hook that plugins (e.g. CAPTCHA,
122 * custom fields) use to inject content into registration forms.
123 */
124 do_action( 'register_form' );
125 ?>
126
127 <div class="frmx-auth-submit">
128 <button type="submit" id="frmx-register-submit" name="wp-submit" class="button submit fill-brand">
129 <?php esc_html_e( 'Register', 'forumax' ); ?>
130 </button>
131 </div>
132
133 <?php wp_nonce_field( 'forumax_register', 'forumax_register_nonce' ); ?>
134 </form>
135 </div>
136 <?php endif; ?>
137
138 </div>
139
140 <script>
141 ( function() {
142 var wrapper = document.getElementById( '<?php echo esc_js( $wrapper_id ); ?>' );
143 if ( ! wrapper ) {
144 return;
145 }
146
147 var tabs = wrapper.querySelectorAll( '.frmx-auth-tab' );
148 var panels = wrapper.querySelectorAll( '.frmx-auth-panel' );
149
150 tabs.forEach( function( tab ) {
151 tab.addEventListener( 'click', function() {
152 var target = this.getAttribute( 'data-frmx-tab' );
153
154 tabs.forEach( function( t ) {
155 t.classList.remove( 'frmx-auth-tab-active' );
156 } );
157 this.classList.add( 'frmx-auth-tab-active' );
158
159 panels.forEach( function( p ) {
160 if ( p.getAttribute( 'data-frmx-panel' ) === target ) {
161 p.classList.add( 'frmx-auth-panel-active' );
162 } else {
163 p.classList.remove( 'frmx-auth-panel-active' );
164 }
165 } );
166 } );
167 } );
168
169 /* Password visibility toggle */
170 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>';
171 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>';
172
173 wrapper.querySelectorAll( 'input[type="password"]' ).forEach( function( input ) {
174 var container = document.createElement( 'div' );
175 container.className = 'frmx-auth-password-wrap';
176 input.parentNode.insertBefore( container, input );
177 container.appendChild( input );
178
179 var toggleBtn = document.createElement( 'button' );
180 toggleBtn.type = 'button';
181 toggleBtn.className = 'frmx-auth-eye-toggle';
182 toggleBtn.setAttribute( 'aria-label', 'Toggle password visibility' );
183 toggleBtn.innerHTML = eyeShow;
184 container.appendChild( toggleBtn );
185
186 toggleBtn.addEventListener( 'click', function() {
187 var isPassword = input.type === 'password';
188 input.type = isPassword ? 'text' : 'password';
189 this.innerHTML = isPassword ? eyeHide : eyeShow;
190 input.focus();
191 } );
192 } );
193 } )();
194 </script>
195 <?php
196
197 return ob_get_clean();
198 }
199
200 add_shortcode( 'forumax_login_form', 'forumax_login_form_shortcode' );
201
202 /**
203 * Redirect to the referrer page after a failed login attempt.
204 *
205 * @param string $username The username that failed login.
206 *
207 * @return void
208 */
209 function forumax_login_failed_redirect( $username ) {
210 $referrer = wp_get_referer();
211 if ( ! empty( $referrer ) && ! str_contains( $referrer, 'wp-login.php' ) ) {
212 wp_redirect( add_query_arg( 'login', 'failed', $referrer ) );
213 exit;
214 }
215 }
216
217 add_action( 'wp_login_failed', 'forumax_login_failed_redirect' );
218
219 /**
220 * Handle the custom registration form submission.
221 *
222 * Creates a new user with the provided username, email, and password,
223 * auto-logs them in, and redirects to the referring page.
224 *
225 * @return void
226 */
227 function forumax_handle_registration() {
228 // Verify nonce.
229 if ( ! isset( $_POST['forumax_register_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['forumax_register_nonce'] ) ), 'forumax_register' ) ) {
230 wp_die( esc_html__( 'Security check failed.', 'forumax' ), 403 );
231 }
232
233 // Check registration is allowed.
234 if ( ! get_option( 'users_can_register' ) ) {
235 wp_die( esc_html__( 'Registration is currently disabled.', 'forumax' ), 403 );
236 }
237
238 $redirect = ! empty( $_POST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_POST['redirect_to'] ) ) : home_url();
239 $referrer = wp_get_referer();
240 if ( empty( $referrer ) ) {
241 $referrer = $redirect;
242 }
243
244 $user_login = isset( $_POST['user_login'] ) ? sanitize_user( wp_unslash( $_POST['user_login'] ) ) : '';
245 $user_email = isset( $_POST['user_email'] ) ? sanitize_email( wp_unslash( $_POST['user_email'] ) ) : '';
246 $user_pass = isset( $_POST['user_pass'] ) ? $_POST['user_pass'] : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- password must not be sanitized.
247 $user_pass_confirm = isset( $_POST['user_pass_confirm'] ) ? $_POST['user_pass_confirm'] : ''; // phpcs:ignore
248
249 // Validate fields.
250 $errors = array();
251
252 if ( empty( $user_login ) ) {
253 $errors[] = 'empty_username';
254 }
255
256 if ( empty( $user_email ) || ! is_email( $user_email ) ) {
257 $errors[] = 'invalid_email';
258 }
259
260 if ( empty( $user_pass ) ) {
261 $errors[] = 'empty_password';
262 }
263
264 if ( $user_pass !== $user_pass_confirm ) {
265 $errors[] = 'password_mismatch';
266 }
267
268 if ( ! empty( $errors ) ) {
269 wp_safe_redirect( add_query_arg( 'reg_error', implode( ',', $errors ), $referrer ) );
270 exit;
271 }
272
273 // Let external plugins validate registration (e.g. Envato license check).
274 $sbv_valid = apply_filters( 'sbv_validate_registration', true );
275 if ( is_wp_error( $sbv_valid ) ) {
276 wp_safe_redirect( add_query_arg( 'reg_error', $sbv_valid->get_error_code(), $referrer ) );
277 exit;
278 }
279
280 // Create the user.
281 $user_id = wp_create_user( $user_login, $user_pass, $user_email );
282
283 if ( is_wp_error( $user_id ) ) {
284 $error_code = $user_id->get_error_code();
285 wp_safe_redirect( add_query_arg( 'reg_error', $error_code, $referrer ) );
286 exit;
287 }
288
289 // Fire standard WordPress registration action.
290 do_action( 'user_register', $user_id );
291
292 // Auto-login the new user.
293 wp_set_current_user( $user_id );
294 wp_set_auth_cookie( $user_id, true );
295
296 wp_safe_redirect( $redirect );
297 exit;
298 }
299
300 add_action( 'admin_post_nopriv_forumax_register', 'forumax_handle_registration' );
301 add_action( 'admin_post_forumax_register', 'forumax_handle_registration' );
302