PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / trunk
Forumax – AI Powered Advanced Community Forum Plugin vtrunk
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 trunk, at includes/login-form.php

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