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 +114 -21 2.4.0 → 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,19 +38,27 @@
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' );
42 + $wrapper_id = $uid;
33 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 +
34 51 ob_start();
35 52 ?>
36 - <div class="frmx-auth-tabs">
53 + <div class="frmx-auth-tabs" id="<?php echo esc_attr( $wrapper_id ); ?>">
37 54
38 55 <?php if ( $can_register ) : ?>
39 56 <div class="frmx-auth-tabs-header">
40 - <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">
41 58 <?php esc_html_e( 'Sign In', 'forumax' ); ?>
42 59 </button>
43 - <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">
44 61 <?php esc_html_e( 'Create Account', 'forumax' ); ?>
45 62 </button>
46 63 </div>
47 64 <?php endif; ?>
@@ -46,9 +63,14 @@
46 63 </div>
47 64 <?php endif; ?>
48 65
49 66 <!-- Login Tab -->
50 - <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; ?>
51 73 <?php
52 74 $form_html = wp_login_form( array(
53 75 'redirect' => $redirect_url,
54 76 'label_username' => esc_html__( 'Username or Email', 'forumax' ),
@@ -54,13 +76,25 @@
54 76 'label_username' => esc_html__( 'Username or Email', 'forumax' ),
55 77 'label_password' => esc_html__( 'Password', 'forumax' ),
56 78 'label_remember' => esc_html__( 'Remember Me', 'forumax' ),
57 79 'label_log_in' => esc_html__( 'Sign In', 'forumax' ),
58 - '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',
59 85 'remember' => true,
60 86 'echo' => false,
61 87 ) );
62 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 +
63 97 // Add placeholders to core login form fields.
64 98 $username_placeholder = esc_attr__( 'Username or Email', 'forumax' );
65 99 $password_placeholder = esc_attr__( 'Password', 'forumax' );
66 100
@@ -74,8 +108,11 @@
74 108 '$1 placeholder="' . $password_placeholder . '"$2',
75 109 $form_html
76 110 );
77 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 +
78 115 echo $form_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
79 116 ?>
80 117
81 118 <?php
@@ -87,31 +124,38 @@
87 124 </div>
88 125
89 126 <?php if ( $can_register ) : ?>
90 127 <!-- Registration Tab -->
91 - <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; ?>
92 136 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="frmx-auth-register-form">
93 137 <input type="hidden" name="action" value="forumax_register" />
94 138 <input type="hidden" name="redirect_to" value="<?php echo esc_url( $redirect_url ); ?>" />
95 139
96 140 <div class="frmx-auth-field">
97 - <label for="frmx_reg_user_login"><?php esc_html_e( 'Username', 'forumax' ); ?></label>
98 - <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 />
99 143 </div>
100 144
101 145 <div class="frmx-auth-field">
102 - <label for="frmx_reg_user_email"><?php esc_html_e( 'Email', 'forumax' ); ?></label>
103 - <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 />
104 148 </div>
105 149
106 150 <div class="frmx-auth-field">
107 - <label for="frmx_reg_user_pass"><?php esc_html_e( 'Password', 'forumax' ); ?></label>
108 - <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 />
109 153 </div>
110 154
111 155 <div class="frmx-auth-field">
112 - <label for="frmx_reg_user_pass_confirm"><?php esc_html_e( 'Confirm Password', 'forumax' ); ?></label>
113 - <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 />
114 158 </div>
115 159
116 160 <?php
117 161 /**
@@ -123,9 +167,9 @@
123 167 do_action( 'register_form' );
124 168 ?>
125 169
126 170 <div class="frmx-auth-submit">
127 - <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">
128 172 <?php esc_html_e( 'Register', 'forumax' ); ?>
129 173 </button>
130 174 </div>
131 175
@@ -137,9 +181,9 @@
137 181 </div>
138 182
139 183 <script>
140 184 ( function() {
141 - var wrapper = document.querySelector( '.frmx-auth-tabs' );
185 + var wrapper = document.getElementById( '<?php echo esc_js( $wrapper_id ); ?>' );
142 186 if ( ! wrapper ) {
143 187 return;
144 188 }
145 189
@@ -205,11 +249,18 @@
205 249 *
206 250 * @return void
207 251 */
208 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 +
209 260 $referrer = wp_get_referer();
210 261 if ( ! empty( $referrer ) && ! str_contains( $referrer, 'wp-login.php' ) ) {
211 - 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 ) ) );
212 263 exit;
213 264 }
214 265 }
215 266
@@ -215,8 +266,29 @@
215 266
216 267 add_action( 'wp_login_failed', 'forumax_login_failed_redirect' );
217 268
218 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 +/**
219 291 * Handle the custom registration form submission.
220 292 *
221 293 * Creates a new user with the provided username, email, and password,
222 294 * auto-logs them in, and redirects to the referring page.
@@ -263,10 +335,34 @@
263 335 if ( $user_pass !== $user_pass_confirm ) {
264 336 $errors[] = 'password_mismatch';
265 337 }
266 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 +
267 363 if ( ! empty( $errors ) ) {
268 - 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 ) ) );
269 365 exit;
270 366 }
271 367
272 368 // Let external plugins validate registration (e.g. Envato license check).
@@ -283,11 +379,8 @@
283 379 $error_code = $user_id->get_error_code();
284 380 wp_safe_redirect( add_query_arg( 'reg_error', $error_code, $referrer ) );
285 381 exit;
286 382 }
287 -
288 - // Fire standard WordPress registration action.
289 - do_action( 'user_register', $user_id );
290 383
291 384 // Auto-login the new user.
292 385 wp_set_current_user( $user_id );
293 386 wp_set_auth_cookie( $user_id, true );