PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.5.1
Forumax – AI Powered Advanced Community Forum Plugin v2.5.1
2.5.1 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 All 31 releases
← All changes | includes/login-form.php +112 -20 2.4.2 → 2.5.1 View file →
@@ -7,8 +7,10 @@
7 7 *
8 8 * @package Forumax
9 9 */
10 10
11 +defined( 'ABSPATH' ) || exit;
12 +
11 13 /**
12 14 * Render the tabbed login and registration form.
13 15 *
14 16 * @param array $atts Shortcode attributes.
@@ -20,8 +22,15 @@
20 22 if ( is_user_logged_in() ) {
21 23 return '<div class="frmx-auth-logged-in">' . esc_html__( 'You are already logged in.', 'forumax' ) . '</div>';
22 24 }
23 25
26 + // Same shortcode is used on several spots of one page (reply prompt, topic
27 + // prompt, subscribe modal, page content). Each instance needs its own IDs
28 + // so a later form is not dropped and tabs/labels stay wired to that form.
29 + static $frmx_login_form_instance = 0;
30 + $frmx_login_form_instance++;
31 + $uid = 'frmx-auth-' . $frmx_login_form_instance;
32 +
24 33 // Extract shortcode attributes with defaults.
25 34 $atts = shortcode_atts( array(
26 35 'redirect' => '',
27 36 ), $atts );
@@ -29,10 +38,17 @@
29 38 // Set redirect URL.
30 39 $redirect_url = ! empty( $atts['redirect'] ) ? $atts['redirect'] : get_permalink();
31 40
32 41 $can_register = get_option( 'users_can_register' );
33 - $wrapper_id = uniqid( 'frmx-auth-tabs-' );
42 + $wrapper_id = $uid;
34 43
44 + // Errors passed back by forumax_login_failed_redirect() / forumax_handle_registration().
45 + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only display flags.
46 + $login_failed = isset( $_GET['login'] ) && 'failed' === sanitize_key( wp_unslash( $_GET['login'] ) );
47 + $reg_errors = isset( $_GET['reg_error'] ) ? array_filter( array_map( 'sanitize_key', explode( ',', sanitize_text_field( wp_unslash( $_GET['reg_error'] ) ) ) ) ) : array();
48 + // phpcs:enable
49 + $active_tab = ( $can_register && ! empty( $reg_errors ) ) ? 'register' : 'login';
50 +
35 51 ob_start();
36 52 ?>
37 53 <div class="frmx-auth-tabs" id="<?php echo esc_attr( $wrapper_id ); ?>">
38 54
@@ -37,12 +53,12 @@
37 53 <div class="frmx-auth-tabs" id="<?php echo esc_attr( $wrapper_id ); ?>">
38 54
39 55 <?php if ( $can_register ) : ?>
40 56 <div class="frmx-auth-tabs-header">
41 - <button type="button" class="frmx-auth-tab frmx-auth-tab-active" data-frmx-tab="login">
57 + <button type="button" class="frmx-auth-tab<?php echo 'login' === $active_tab ? ' frmx-auth-tab-active' : ''; ?>" data-frmx-tab="login">
42 58 <?php esc_html_e( 'Sign In', 'forumax' ); ?>
43 59 </button>
44 - <button type="button" class="frmx-auth-tab" data-frmx-tab="register">
60 + <button type="button" class="frmx-auth-tab<?php echo 'register' === $active_tab ? ' frmx-auth-tab-active' : ''; ?>" data-frmx-tab="register">
45 61 <?php esc_html_e( 'Create Account', 'forumax' ); ?>
46 62 </button>
47 63 </div>
48 64 <?php endif; ?>
@@ -47,9 +63,14 @@
47 63 </div>
48 64 <?php endif; ?>
49 65
50 66 <!-- Login Tab -->
51 - <div class="frmx-auth-panel frmx-auth-panel-active" data-frmx-panel="login">
67 + <div class="frmx-auth-panel<?php echo 'login' === $active_tab ? ' frmx-auth-panel-active' : ''; ?>" data-frmx-panel="login">
68 + <?php if ( $login_failed ) : ?>
69 + <div class="frmx-auth-error bbp-template-notice error" role="alert">
70 + <p><?php esc_html_e( 'Invalid username, email address or password. Please try again.', 'forumax' ); ?></p>
71 + </div>
72 + <?php endif; ?>
52 73 <?php
53 74 $form_html = wp_login_form( array(
54 75 'redirect' => $redirect_url,
55 76 'label_username' => esc_html__( 'Username or Email', 'forumax' ),
@@ -55,13 +76,25 @@
55 76 'label_username' => esc_html__( 'Username or Email', 'forumax' ),
56 77 'label_password' => esc_html__( 'Password', 'forumax' ),
57 78 'label_remember' => esc_html__( 'Remember Me', 'forumax' ),
58 79 'label_log_in' => esc_html__( 'Sign In', 'forumax' ),
59 - 'form_id' => 'forumax_login_form',
80 + 'form_id' => $uid . '-login',
81 + 'id_username' => $uid . '-user',
82 + 'id_password' => $uid . '-pass',
83 + 'id_remember' => $uid . '-remember',
84 + 'id_submit' => $uid . '-submit',
60 85 'remember' => true,
61 86 'echo' => false,
62 87 ) );
63 88
89 + // Class hook for styling (keeps a stable selector with form_id).
90 + $form_html = preg_replace(
91 + '/<form\b/i',
92 + '<form class="forumax_login_form"',
93 + $form_html,
94 + 1
95 + );
96 +
64 97 // Add placeholders to core login form fields.
65 98 $username_placeholder = esc_attr__( 'Username or Email', 'forumax' );
66 99 $password_placeholder = esc_attr__( 'Password', 'forumax' );
67 100
@@ -75,8 +108,11 @@
75 108 '$1 placeholder="' . $password_placeholder . '"$2',
76 109 $form_html
77 110 );
78 111
112 + // Marks submissions from this form so only they are redirected back on failure.
113 + $form_html = preg_replace( '/<\/form>/i', '<input type="hidden" name="forumax_login" value="1" /></form>', $form_html, 1 );
114 +
79 115 echo $form_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
80 116 ?>
81 117
82 118 <?php
@@ -88,31 +124,38 @@
88 124 </div>
89 125
90 126 <?php if ( $can_register ) : ?>
91 127 <!-- Registration Tab -->
92 - <div class="frmx-auth-panel" data-frmx-panel="register">
128 + <div class="frmx-auth-panel<?php echo 'register' === $active_tab ? ' frmx-auth-panel-active' : ''; ?>" data-frmx-panel="register">
129 + <?php if ( ! empty( $reg_errors ) ) : ?>
130 + <div class="frmx-auth-error bbp-template-notice error" role="alert">
131 + <?php foreach ( $reg_errors as $reg_error ) : ?>
132 + <p><?php echo esc_html( forumax_registration_error_message( $reg_error ) ); ?></p>
133 + <?php endforeach; ?>
134 + </div>
135 + <?php endif; ?>
93 136 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="frmx-auth-register-form">
94 137 <input type="hidden" name="action" value="forumax_register" />
95 138 <input type="hidden" name="redirect_to" value="<?php echo esc_url( $redirect_url ); ?>" />
96 139
97 140 <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 />
141 + <label for="<?php echo esc_attr( $uid ); ?>-reg-login"><?php esc_html_e( 'Username', 'forumax' ); ?></label>
142 + <input type="text" name="user_login" id="<?php echo esc_attr( $uid ); ?>-reg-login" placeholder="<?php esc_attr_e( 'Username', 'forumax' ); ?>" autocomplete="username" required />
100 143 </div>
101 144
102 145 <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 />
146 + <label for="<?php echo esc_attr( $uid ); ?>-reg-email"><?php esc_html_e( 'Email', 'forumax' ); ?></label>
147 + <input type="email" name="user_email" id="<?php echo esc_attr( $uid ); ?>-reg-email" placeholder="<?php esc_attr_e( 'Email', 'forumax' ); ?>" autocomplete="email" required />
105 148 </div>
106 149
107 150 <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 />
151 + <label for="<?php echo esc_attr( $uid ); ?>-reg-pass"><?php esc_html_e( 'Password', 'forumax' ); ?></label>
152 + <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 />
110 153 </div>
111 154
112 155 <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 />
156 + <label for="<?php echo esc_attr( $uid ); ?>-reg-pass-confirm"><?php esc_html_e( 'Confirm Password', 'forumax' ); ?></label>
157 + <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 />
115 158 </div>
116 159
117 160 <?php
118 161 /**
@@ -124,9 +167,9 @@
124 167 do_action( 'register_form' );
125 168 ?>
126 169
127 170 <div class="frmx-auth-submit">
128 - <button type="submit" id="frmx-register-submit" name="wp-submit" class="button submit fill-brand">
171 + <button type="submit" id="<?php echo esc_attr( $uid ); ?>-register" name="wp-submit" class="button submit fill-brand">
129 172 <?php esc_html_e( 'Register', 'forumax' ); ?>
130 173 </button>
131 174 </div>
132 175
@@ -206,11 +249,18 @@
206 249 *
207 250 * @return void
208 251 */
209 252 function forumax_login_failed_redirect( $username ) {
253 + // Only handle the Forumax form; other login forms (WooCommerce, REST,
254 + // XML-RPC, wp-login.php) keep their own error handling.
255 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- core login has no nonce; this only picks the redirect target.
256 + if ( empty( $_POST['forumax_login'] ) ) {
257 + return;
258 + }
259 +
210 260 $referrer = wp_get_referer();
211 261 if ( ! empty( $referrer ) && ! str_contains( $referrer, 'wp-login.php' ) ) {
212 - wp_redirect( add_query_arg( 'login', 'failed', $referrer ) );
262 + wp_safe_redirect( add_query_arg( 'login', 'failed', remove_query_arg( array( 'login', 'reg_error' ), $referrer ) ) );
213 263 exit;
214 264 }
215 265 }
216 266
@@ -216,8 +266,29 @@
216 266
217 267 add_action( 'wp_login_failed', 'forumax_login_failed_redirect' );
218 268
219 269 /**
270 + * Human readable message for a registration error code.
271 + *
272 + * @param string $code Error code.
273 + * @return string
274 + */
275 +function forumax_registration_error_message( $code ) {
276 + $messages = array(
277 + 'empty_username' => __( 'Please enter a username.', 'forumax' ),
278 + 'invalid_username' => __( 'This username is invalid. Please use letters, numbers and - _ . @ only.', 'forumax' ),
279 + 'existing_user_login' => __( 'This username is already registered. Please choose another one.', 'forumax' ),
280 + 'invalid_email' => __( 'Please enter a valid email address.', 'forumax' ),
281 + 'email_exists' => __( 'This email address is already registered.', 'forumax' ),
282 + 'existing_user_email' => __( 'This email address is already registered.', 'forumax' ),
283 + 'empty_password' => __( 'Please enter a password.', 'forumax' ),
284 + 'password_mismatch' => __( 'The passwords do not match.', 'forumax' ),
285 + );
286 +
287 + return $messages[ $code ] ?? __( 'Registration failed. Please check the form and try again.', 'forumax' );
288 +}
289 +
290 +/**
220 291 * Handle the custom registration form submission.
221 292 *
222 293 * Creates a new user with the provided username, email, and password,
223 294 * auto-logs them in, and redirects to the referring page.
@@ -264,10 +335,34 @@
264 335 if ( $user_pass !== $user_pass_confirm ) {
265 336 $errors[] = 'password_mismatch';
266 337 }
267 338
339 + if ( empty( $errors ) ) {
340 + if ( ! validate_username( $user_login ) ) {
341 + $errors[] = 'invalid_username';
342 + } elseif ( username_exists( $user_login ) ) {
343 + $errors[] = 'existing_user_login';
344 + }
345 +
346 + if ( email_exists( $user_email ) ) {
347 + $errors[] = 'email_exists';
348 + }
349 + }
350 +
351 + // Let CAPTCHA / anti-spam plugins that print into `register_form`
352 + // validate the submission too, as core registration does.
353 + if ( empty( $errors ) ) {
354 + $wp_errors = new WP_Error();
355 + do_action( 'register_post', $user_login, $user_email, $wp_errors );
356 + $wp_errors = apply_filters( 'registration_errors', $wp_errors, $user_login, $user_email );
357 +
358 + if ( is_wp_error( $wp_errors ) && $wp_errors->has_errors() ) {
359 + $errors = array_merge( $errors, $wp_errors->get_error_codes() );
360 + }
361 + }
362 +
268 363 if ( ! empty( $errors ) ) {
269 - wp_safe_redirect( add_query_arg( 'reg_error', implode( ',', $errors ), $referrer ) );
364 + wp_safe_redirect( add_query_arg( 'reg_error', implode( ',', array_unique( $errors ) ), remove_query_arg( array( 'login', 'reg_error' ), $referrer ) ) );
270 365 exit;
271 366 }
272 367
273 368 // Let external plugins validate registration (e.g. Envato license check).
@@ -284,11 +379,8 @@
284 379 $error_code = $user_id->get_error_code();
285 380 wp_safe_redirect( add_query_arg( 'reg_error', $error_code, $referrer ) );
286 381 exit;
287 382 }
288 -
289 - // Fire standard WordPress registration action.
290 - do_action( 'user_register', $user_id );
291 383
292 384 // Auto-login the new user.
293 385 wp_set_current_user( $user_id );
294 386 wp_set_auth_cookie( $user_id, true );