PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Shortcodes / class-shortcodes.php
wp-2fa / includes / classes / Shortcodes Last commit date
class-shortcodes.php 1 day ago index.php 1 day ago
class-shortcodes.php
306 lines
1 <?php
2 /**
3 * Responsible for rendering the short codes.
4 *
5 * @package wp2fa
6 * @subpackage short-codes
7 * @copyright 2026 Melapress
8 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
9 * @link https://wordpress.org/plugins/wp-2fa/
10 */
11
12 declare(strict_types=1);
13
14 namespace WP2FA\Shortcodes;
15
16 use WP2FA\Core;
17 use WP2FA\WP2FA;
18 use WP2FA\Admin\User_Notices;
19 use WP2FA\Admin\Wizard_Integration;
20 use WP2FA\Utils\Settings_Utils;
21 use WP2FA\Admin\Helpers\WP_Helper;
22 use WP2FA\Admin\Views\Re_Login_2FA;
23 use WP2FA\Admin\Helpers\User_Helper;
24
25 if ( ! class_exists( '\WP2FA\Shortcodes\Shortcodes' ) ) {
26 /**
27 * Class for rendering shortcodes.
28 */
29 class Shortcodes {
30
31 /**
32 * Constructor.
33 */
34 public static function init() {
35 \add_shortcode( 'wp-2fa-setup-form', array( __CLASS__, 'user_setup_2fa_form' ) );
36 \add_shortcode( 'wp-2fa-setup-notice', array( __CLASS__, 'user_setup_2fa_notice' ) );
37 //\add_action( 'wp_enqueue_scripts', array( __CLASS__, 'register_2fa_shortcode_scripts' ) );
38
39 \add_filter( 'the_content', array( __CLASS__, 'maybe_hide_generated_by_text' ) );
40 }
41
42 /**
43 * Strips the "Page generated by WP 2FA Plugin" attribution text from the
44 * custom frontend 2FA page when the hide_page_generated_by setting is enabled.
45 *
46 * @param string $content The post content.
47 *
48 * @return string
49 *
50 * @since 2.8.0
51 */
52 public static function maybe_hide_generated_by_text( $content ) {
53 if ( ! \is_page() ) {
54 return $content;
55 }
56
57 if ( ! \has_shortcode( $content, 'wp-2fa-setup-form' ) ) {
58 return $content;
59 }
60
61 if ( ! WP2FA::get_wp2fa_white_label_setting( 'hide_page_generated_by' ) ) {
62 return $content;
63 }
64
65 // Remove the attribution paragraph that was embedded in the page content at creation time.
66 $content = \preg_replace(
67 '#<p>\s*' . \preg_quote( \esc_html__( 'Page generated by', 'wp-2fa' ), '#' ) . '\s*<a [^>]*>.*?</a>\s*</p>#s',
68 '',
69 $content
70 );
71
72 return $content;
73 }
74
75 /**
76 * Register scripts and styles.
77 */
78 public static function register_2fa_shortcode_scripts() {
79 // Add our front end stuff, which we only want to load when the shortcode is present.
80 \wp_register_script( 'wp_2fa_frontend_scripts', Core\script_url( 'wp-2fa', 'admin' ), array( 'jquery', 'wp_2fa_micro_modals', 'wp-i18n' ), WP_2FA_VERSION, true );
81 \wp_register_script( 'wp_2fa_micro_modals', Core\script_url( 'micromodal', 'admin' ), array(), WP_2FA_VERSION, true );
82 \wp_register_style( 'wp_2fa_styles', Core\style_url( 'styles', 'frontend' ), array(), WP_2FA_VERSION );
83
84 $data_array = array(
85 'ajaxURL' => \admin_url( 'admin-ajax.php' ),
86 'roles' => WP_Helper::get_roles_wp(),
87 'nonce' => \wp_create_nonce( 'wp-2fa-settings-nonce' ),
88 'codesPreamble' => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
89 'readyText' => \esc_html__( 'I\'m ready', 'wp-2fa' ),
90 'codeReSentText' => \esc_html__( 'New code sent', 'wp-2fa' ),
91 'allDoneHeading' => \esc_html__( 'All done.', 'wp-2fa' ),
92 'allDoneText' => \esc_html__( 'Your login just got more secure.', 'wp-2fa' ),
93 'closeWizard' => \esc_html__( 'Close Wizard', 'wp-2fa' ),
94 'invalidEmail' => \esc_html__( 'Please use a valid email address', 'wp-2fa' ),
95 );
96 \wp_localize_script( 'wp_2fa_frontend_scripts', 'wp2faData', $data_array );
97
98 $role = User_Helper::get_user_role();
99
100 $re_login = Settings_Utils::get_setting_role( $role, Re_Login_2FA::RE_LOGIN_SETTINGS_NAME );
101
102 $data_array = array(
103 'ajaxURL' => \admin_url( 'admin-ajax.php' ),
104 'nonce' => \wp_create_nonce( 'wp2fa-verify-wizard-page' ),
105 'codesPreamble' => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
106 'readyText' => \esc_html__( 'I\'m ready', 'wp-2fa' ),
107 'codeReSentText' => \esc_html__( 'New code sent', 'wp-2fa' ),
108 'invalidEmail' => \esc_html__( 'Please use a valid email address', 'wp-2fa' ),
109 'backupCodesSent' => \esc_html__( 'Backup codes sent', 'wp-2fa' ),
110 'reLogin' => $re_login,
111 'reLoginEnabled' => Re_Login_2FA::ENABLED_SETTING_VALUE,
112 'loginUrl' => \wp_login_url(),
113 );
114 $redirect_page = \sanitize_text_field( Settings_Utils::get_setting_role( $role, 'redirect-user-custom-page' ) );
115 $redirect_page_global = \sanitize_text_field( Settings_Utils::get_setting_role( null, 'redirect-user-custom-page' ) );
116 $redirect_page_global_setting = \sanitize_text_field( Settings_Utils::get_setting_role( $role, 'redirect-user-custom-page-global' ) );
117
118 // Priority: role-specific redirect-user-custom-page > global redirect-user-custom-page > redirect-user-custom-page-global > empty.
119 if ( '' !== trim( (string) $redirect_page ) ) {
120 $data_array['redirectToUrl'] = \trailingslashit( get_site_url() ) . $redirect_page;
121 } elseif ( '' !== trim( (string) $redirect_page_global ) ) {
122 $data_array['redirectToUrl'] = \trailingslashit( get_site_url() ) . $redirect_page_global;
123 } elseif ( '' !== trim( (string) $redirect_page_global_setting ) ) {
124 $data_array['redirectToUrl'] = \trailingslashit( get_site_url() ) . $redirect_page_global_setting;
125 } else {
126 $data_array['redirectToUrl'] = '';
127 }
128
129 // Check for shortcode parameter - if one is present use it to redirect the user - highest priority.
130 if ( isset( $redirect_after ) && ! empty( $redirect_after ) ) {
131 $candidate_url = \trailingslashit( \get_site_url() ) . \sanitize_text_field( $redirect_after );
132 $validated = \wp_validate_redirect( $candidate_url, '' );
133 if ( ! empty( $validated ) ) {
134 $data_array['redirectToUrl'] = $validated;
135 }
136 } elseif ( isset( $_GET['return'] ) && ! empty( $_GET['return'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
137 $return_path = \sanitize_text_field( \wp_unslash( $_GET['return'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
138 $candidate_url = \trailingslashit( \get_site_url() ) . $return_path;
139 $validated = \wp_validate_redirect( $candidate_url, '' );
140 if ( ! empty( $validated ) ) {
141 $data_array['redirectToUrl'] = $validated;
142 }
143 }
144
145 \wp_localize_script( 'wp_2fa_frontend_scripts', 'wp2faWizardData', $data_array );
146 }
147
148 /**
149 * Output setup form.
150 *
151 * @param array $atts - Array with the attributes passed to shortcode.
152 *
153 * @return string
154 */
155 public static function user_setup_2fa_form( $atts ) {
156 $atts = \shortcode_atts(
157 array(
158 'show_preamble' => 'true',
159 'redirect_after' => '',
160 'do_not_show_enabled' => 'false',
161 ),
162 $atts
163 );
164
165 $show_preamble = filter_var( $atts['show_preamble'], FILTER_VALIDATE_BOOLEAN );
166 $redirect_after = \sanitize_text_field( $atts['redirect_after'] );
167 $do_not_show_enabled = filter_var( $atts['do_not_show_enabled'], FILTER_VALIDATE_BOOLEAN );
168
169 /**
170 * Fires when the FE shortcode scripts are registered.
171 *
172 * @param bool $shortcodes - True if called from the short codes method.
173 *
174 * @since 2.2.0
175 */
176 \do_action( WP_2FA_PREFIX . 'shortcode_scripts', true );
177
178 if ( \is_user_logged_in() ) {
179
180 $additional_args = array(
181 'is_shortcode' => true,
182 'show_preamble' => $show_preamble,
183 'options' => array(
184 'do_not_show_enabled' => $do_not_show_enabled,
185 ),
186 );
187
188 $user = \wp_get_current_user();
189
190 ob_start();
191 echo self::get_notification_style(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
192 echo '<div class="wp-2fa-shortcode-wrapper">';
193 User_Notices::user_setup_2fa_nag( 'output_shortcode' );
194 Wizard_Integration::render_profile_section_frontend( $user, $additional_args );
195 echo '</div>';
196 $content = ob_get_contents();
197 ob_end_clean();
198
199 return $content;
200 } elseif ( ! \is_admin() && ! \is_user_logged_in() ) {
201 ob_start();
202 $new_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
203 $redirect_to = ! empty( $new_page_id ) ? \get_permalink( $new_page_id ) : \get_home_url();
204 $link_markup = '<a href="' . \esc_url( \wp_login_url( $redirect_to ) ) . '">' . \esc_html__( 'Login here.', 'wp-2fa' ) . '</a>';
205 $message = '<p id="wp_2fa_login_to_view_text">' . str_replace( '{login_url}', $link_markup, WP2FA::get_wp2fa_white_label_setting( 'login-to-view-area', true ) ) . '</p>';
206 echo \wp_kses_post( $message );
207 $content = ob_get_contents();
208 ob_end_clean();
209 return $content;
210 }
211 }
212
213 /**
214 * Output setup nag.
215 *
216 * @param array $atts - Array with the attributes passed to shortcode.
217 *
218 * @return string
219 */
220 public static function user_setup_2fa_notice( $atts ) {
221 $atts = \shortcode_atts(
222 array(
223 'configure_2fa_url' => '',
224 ),
225 $atts
226 );
227
228 $configure_2fa_url = \esc_url_raw( $atts['configure_2fa_url'] );
229
230 // TODO: is that really necessary?
231 User_Notices::init();
232
233 if ( ! is_admin() && is_user_logged_in() ) {
234 \wp_enqueue_script( 'wp_2fa_micro_modals' );
235 \wp_enqueue_script( 'wp_2fa_frontend_scripts' );
236 \wp_enqueue_style( 'wp_2fa_styles' );
237
238 $data_array = array(
239 'ajaxURL' => \admin_url( 'admin-ajax.php' ),
240 'roles' => WP_Helper::get_roles_wp(),
241 'nonce' => \wp_create_nonce( 'wp-2fa-settings-nonce' ),
242 'codesPreamble' => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
243 'readyText' => \esc_html__( 'I\'m ready', 'wp-2fa' ),
244 'codeReSentText' => \esc_html__( 'New code sent', 'wp-2fa' ),
245 'allDoneHeading' => \esc_html__( 'All done.', 'wp-2fa' ),
246 'allDoneText' => \esc_html__( 'Your login just got more secure.', 'wp-2fa' ),
247 'closeWizard' => \esc_html__( 'Close Wizard', 'wp-2fa' ),
248 'redirectToUrl' => $configure_2fa_url,
249 );
250 \wp_localize_script( 'wp_2fa_frontend_scripts', 'wp2faData', $data_array );
251
252 ob_start();
253 User_Notices::user_setup_2fa_nag( 'output_shortcode', $configure_2fa_url );
254 $content = ob_get_contents();
255 ob_end_clean();
256
257 return $content;
258 }
259
260 return '';
261 }
262
263 /**
264 * Output the style of the nags on FE pages.
265 *
266 * @return string
267 *
268 * @since 4.0.0
269 */
270 public static function get_notification_style(): string {
271 $styles = <<<CSS
272 <style media="screen">
273 .wp-2fa-shortcode-wrapper .wp-2fa-nag, .woocommerce .wp-2fa-nag {
274 width: 100%;
275 border: 1px solid #eee;
276 border-left: 3px solid red;
277 padding: 12px 15px;
278 margin-bottom: 20px;
279 font-size: 13px;
280 }
281 .wp-2fa-shortcode-wrapper .wp-2fa-nag p, .woocommerce .wp-2fa-nag p {
282 margin-bottom: 5px
283 }
284 .wp-2fa-shortcode-wrapper .wp-2fa-nag br + span, .woocommerce .wp-2fa-nag br + span {
285 clear: both;
286 display: block;
287 margin-top: 10px;
288 margin-bottom: 10px;
289 }
290 .wp-2fa-shortcode-wrapper .wp-2fa-nag a.button, .woocommerce .wp-2fa-nag a.button {
291 font-size: 13px;
292 padding: 0 !important;
293 margin-right: 20px;
294 background-color: transparent;
295 }
296 .wp-2fa-shortcode-wrapper .wp-2fa-nag a.button:visited, .woocommerce .wp-2fa-nag a.button:visited {
297 color: #222;
298 }
299 </style>
300 CSS;
301
302 return $styles;
303 }
304 }
305 }
306