Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 224 |
|
0.00% |
0 / 19 |
CRAP | |
0.00% |
0 / 1 |
| Blocked_Login_Page | |
0.00% |
0 / 224 |
|
0.00% |
0 / 19 |
6480 | |
0.00% |
0 / 1 |
| instance | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| __construct | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| get_help_url | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
| add_args_to_lostpassword_redirect_url | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
30 | |||
| add_args_to_lostpassword_url | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
| add_args_to_login_post_url | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
42 | |||
| add_args_to_login_url | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
42 | |||
| check_valid_blocked_user | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
| is_blocked_user_valid | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
| is_valid_protect_recovery_key | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
20 | |||
| render_and_die | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
90 | |||
| render_blocked_login_message | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| process_recovery_email | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
| send_recovery_email | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
90 | |||
| protect_die | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
| render_recovery_form | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| render_recovery_success | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| get_html_blocked_login_message | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| get_html_recovery_form | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
| display_page | |
0.00% |
0 / 61 |
|
0.00% |
0 / 1 |
132 | |||
| 1 | <?php // phpcs:ignore - WordPress.Files.FileName.InvalidClassFileName |
| 2 | |
| 3 | namespace Automattic\Jetpack\Waf; |
| 4 | |
| 5 | use Automattic\Jetpack\Connection\Client; |
| 6 | use Jetpack_Options; |
| 7 | use WP_Error; |
| 8 | use WP_User; |
| 9 | |
| 10 | /** |
| 11 | * Class Blocked_Login_Page |
| 12 | * |
| 13 | * Instantiated on the wp-login page when Jetpack modules are loaded and $pagenow |
| 14 | * is available, or during the login_head hook. |
| 15 | * |
| 16 | * Class will only be instantiated if a hard blocked IP address is detected. |
| 17 | */ |
| 18 | abstract class Blocked_Login_Page { |
| 19 | |
| 20 | /** |
| 21 | * Instance of the class. |
| 22 | * |
| 23 | * @var Blocked_Login_Page |
| 24 | */ |
| 25 | private static $instance = null; |
| 26 | |
| 27 | /** |
| 28 | * Can send recovery emails. defaults to true. |
| 29 | * |
| 30 | * @var bool |
| 31 | */ |
| 32 | public $can_send_recovery_emails; |
| 33 | |
| 34 | /** |
| 35 | * The IP address. |
| 36 | * |
| 37 | * @var string |
| 38 | */ |
| 39 | public $ip_address; |
| 40 | |
| 41 | /** |
| 42 | * Valid blocked user ID. |
| 43 | * |
| 44 | * @var int |
| 45 | */ |
| 46 | public $valid_blocked_user_id; |
| 47 | |
| 48 | /** |
| 49 | * The email address. |
| 50 | * |
| 51 | * @var string |
| 52 | */ |
| 53 | public $email_address; |
| 54 | |
| 55 | /** |
| 56 | * Status code for too many requests. |
| 57 | * |
| 58 | * @var int |
| 59 | */ |
| 60 | const HTTP_STATUS_CODE_TOO_MANY_REQUESTS = 429; |
| 61 | |
| 62 | /** |
| 63 | * Singleton implementation |
| 64 | * |
| 65 | * @param string $ip_address - the IP address. |
| 66 | * |
| 67 | * @return object |
| 68 | */ |
| 69 | public static function instance( $ip_address ) { |
| 70 | if ( ! self::$instance ) { |
| 71 | // @phan-suppress-next-line PhanTypeInstantiateAbstractStatic -- This is only instantiated in non-abstract classes (i.e. Waf_Blocked_Login_Page). |
| 72 | self::$instance = new static( $ip_address ); |
| 73 | } |
| 74 | |
| 75 | return self::$instance; |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Singleton implementation |
| 80 | * |
| 81 | * @param string $ip_address - the IP address. |
| 82 | */ |
| 83 | public function __construct( $ip_address ) { |
| 84 | /** |
| 85 | * Filter controls if an email recovery form is shown to blocked IPs. |
| 86 | * |
| 87 | * A recovery form allows folks to re-gain access to the login form |
| 88 | * via an email link if their IP was mistakenly blocked. |
| 89 | * |
| 90 | * @module protect |
| 91 | * |
| 92 | * @since 5.6.0 |
| 93 | * |
| 94 | * @param bool $can_send_recovery_emails Defaults to true. |
| 95 | */ |
| 96 | $this->can_send_recovery_emails = apply_filters( 'jetpack_protect_can_send_recovery_emails', true ); |
| 97 | $this->ip_address = $ip_address; |
| 98 | |
| 99 | add_filter( 'wp_authenticate_user', array( $this, 'check_valid_blocked_user' ), 10, 1 ); |
| 100 | add_filter( 'site_url', array( $this, 'add_args_to_login_post_url' ), 10, 3 ); |
| 101 | add_filter( 'network_site_url', array( $this, 'add_args_to_login_post_url' ), 10, 3 ); |
| 102 | add_filter( 'lostpassword_url', array( $this, 'add_args_to_lostpassword_url' ), 10, 2 ); |
| 103 | add_filter( 'login_url', array( $this, 'add_args_to_login_url' ), 10, 3 ); |
| 104 | add_filter( 'lostpassword_redirect', array( $this, 'add_args_to_lostpassword_redirect_url' ), 10, 1 ); |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Gets the URL that redirects to the support page on unblocking |
| 109 | * |
| 110 | * @since 8.5.0 |
| 111 | * |
| 112 | * @return string |
| 113 | */ |
| 114 | abstract public function get_help_url(); |
| 115 | |
| 116 | /** |
| 117 | * Add arguments to lost password redirect url. |
| 118 | * |
| 119 | * @param string $url - the URL. |
| 120 | */ |
| 121 | public function add_args_to_lostpassword_redirect_url( $url ) { |
| 122 | if ( $this->valid_blocked_user_id ) { |
| 123 | $url = empty( $url ) ? wp_login_url() : $url; |
| 124 | $url = add_query_arg( |
| 125 | array( |
| 126 | 'validate_jetpack_protect_recovery' => isset( $_GET['validate_jetpack_protect_recovery'] ) ? filter_var( wp_unslash( $_GET['validate_jetpack_protect_recovery'] ) ) : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 127 | 'user_id' => isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 128 | 'checkemail' => 'confirm', |
| 129 | ), |
| 130 | $url |
| 131 | ); |
| 132 | } |
| 133 | |
| 134 | return $url; |
| 135 | } |
| 136 | |
| 137 | /** |
| 138 | * Add arguments to lost password redirect url. |
| 139 | * |
| 140 | * @param string $url - the URL. |
| 141 | * @param string $redirect - where to redirect to. |
| 142 | */ |
| 143 | public function add_args_to_lostpassword_url( $url, $redirect ) { |
| 144 | if ( $this->valid_blocked_user_id ) { |
| 145 | $args = array( |
| 146 | 'validate_jetpack_protect_recovery' => isset( $_GET['validate_jetpack_protect_recovery'] ) ? filter_var( wp_unslash( $_GET['validate_jetpack_protect_recovery'] ) ) : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 147 | 'user_id' => isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 148 | 'action' => 'lostpassword', |
| 149 | ); |
| 150 | if ( ! empty( $redirect ) ) { |
| 151 | $args['redirect_to'] = $redirect; |
| 152 | } |
| 153 | $url = add_query_arg( $args, $url ); |
| 154 | } |
| 155 | |
| 156 | return $url; |
| 157 | } |
| 158 | |
| 159 | /** |
| 160 | * Add arguments to login post url. |
| 161 | * |
| 162 | * @param string $url - the URL. |
| 163 | * @param string $path - the path. |
| 164 | * @param string $scheme -the scheme(?). |
| 165 | */ |
| 166 | public function add_args_to_login_post_url( $url, $path, $scheme ) { |
| 167 | if ( $this->valid_blocked_user_id && ( 'login_post' === $scheme || 'login' === $scheme ) ) { |
| 168 | $url = add_query_arg( |
| 169 | array( |
| 170 | 'validate_jetpack_protect_recovery' => isset( $_GET['validate_jetpack_protect_recovery'] ) ? filter_var( wp_unslash( $_GET['validate_jetpack_protect_recovery'] ) ) : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 171 | 'user_id' => isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 172 | ), |
| 173 | $url |
| 174 | ); |
| 175 | |
| 176 | } |
| 177 | |
| 178 | return $url; |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * Add arguments to login post url. |
| 183 | * |
| 184 | * @param string $url - the URL. |
| 185 | * @param string $redirect - where we want to redirect to. |
| 186 | * @param string $force_reauth -if we're forcing reauthorization. |
| 187 | */ |
| 188 | public function add_args_to_login_url( $url, $redirect, $force_reauth ) { |
| 189 | if ( $this->valid_blocked_user_id ) { |
| 190 | $args = array( |
| 191 | 'validate_jetpack_protect_recovery' => isset( $_GET['validate_jetpack_protect_recovery'] ) ? filter_var( wp_unslash( $_GET['validate_jetpack_protect_recovery'] ) ) : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 192 | 'user_id' => isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 193 | ); |
| 194 | |
| 195 | if ( ! empty( $redirect ) ) { |
| 196 | $args['redirect_to'] = $redirect; |
| 197 | } |
| 198 | |
| 199 | if ( ! empty( $force_reauth ) ) { |
| 200 | $args['reauth'] = '1'; |
| 201 | } |
| 202 | $url = add_query_arg( $args, $url ); |
| 203 | } |
| 204 | |
| 205 | return $url; |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * Check if user is blocked. |
| 210 | * |
| 211 | * @param WP_User|WP_Error $user - The user or error object if prior callback failed auth. |
| 212 | */ |
| 213 | public function check_valid_blocked_user( $user ) { |
| 214 | if ( is_wp_error( $user ) ) { |
| 215 | return $user; |
| 216 | } |
| 217 | |
| 218 | if ( $this->valid_blocked_user_id && $this->valid_blocked_user_id != $user->ID ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual |
| 219 | return new WP_Error( 'invalid_recovery_token', __( 'The recovery token is not valid for this user.', 'jetpack-waf' ) ); |
| 220 | } |
| 221 | |
| 222 | return $user; |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * Check if user is valid. |
| 227 | */ |
| 228 | public function is_blocked_user_valid() { |
| 229 | if ( ! $this->can_send_recovery_emails ) { |
| 230 | return false; |
| 231 | } |
| 232 | |
| 233 | if ( $this->valid_blocked_user_id ) { |
| 234 | return true; |
| 235 | } |
| 236 | |
| 237 | if ( ! isset( $_GET['validate_jetpack_protect_recovery'] ) || ! isset( $_GET['user_id'] ) ) { // phpcs:ignore: WordPress.Security.NonceVerification.Recommended -- no changes made if this isn't set. |
| 238 | return false; |
| 239 | } |
| 240 | |
| 241 | if ( ! $this->is_valid_protect_recovery_key( filter_var( wp_unslash( $_GET['validate_jetpack_protect_recovery'] ) ), (int) $_GET['user_id'] ) ) { // phpcs:ignore: WordPress.Security.NonceVerification.Recommended -- no changes made if this isn't set. |
| 242 | return false; |
| 243 | } |
| 244 | |
| 245 | $this->valid_blocked_user_id = (int) $_GET['user_id']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nothing on the site is changed in response to this request. |
| 246 | |
| 247 | return true; |
| 248 | } |
| 249 | |
| 250 | /** |
| 251 | * Checks if recovery key is valid. |
| 252 | * |
| 253 | * @param string $key - they recovery key. |
| 254 | * @param int $user_id - the User ID. |
| 255 | */ |
| 256 | public function is_valid_protect_recovery_key( $key, $user_id ) { |
| 257 | |
| 258 | $path = sprintf( '/sites/%d/protect/recovery/confirm', Jetpack_Options::get_option( 'id' ) ); |
| 259 | $response = Client::wpcom_json_api_request_as_blog( |
| 260 | $path, |
| 261 | '1.1', |
| 262 | array( |
| 263 | 'method' => 'post', |
| 264 | ), |
| 265 | array( |
| 266 | 'token' => $key, |
| 267 | 'user_id' => $user_id, |
| 268 | 'ip' => $this->ip_address, |
| 269 | ) |
| 270 | ); |
| 271 | |
| 272 | $result = json_decode( wp_remote_retrieve_body( $response ) ); |
| 273 | |
| 274 | if ( is_wp_error( $result ) || empty( $result ) || isset( $result->error ) ) { |
| 275 | return false; |
| 276 | } |
| 277 | |
| 278 | set_transient( 'jetpack_protect_recovery_key_validated_' . $user_id, true, 600 ); |
| 279 | |
| 280 | return true; |
| 281 | } |
| 282 | |
| 283 | /** |
| 284 | * Check if we should render the recovery form. |
| 285 | */ |
| 286 | public function render_and_die() { |
| 287 | if ( ! $this->can_send_recovery_emails ) { |
| 288 | $this->render_blocked_login_message(); |
| 289 | |
| 290 | return; |
| 291 | } |
| 292 | |
| 293 | if ( isset( $_GET['validate_jetpack_protect_recovery'] ) && ! empty( $_GET['user_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no site changes, just throws invalid token error. |
| 294 | $error = new WP_Error( 'invalid_token', __( "Oops, we couldn't validate the recovery token.", 'jetpack-waf' ) ); |
| 295 | $this->protect_die( $error ); |
| 296 | |
| 297 | return; |
| 298 | } |
| 299 | |
| 300 | if ( |
| 301 | isset( $_GET['jetpack-protect-recovery'] ) && |
| 302 | isset( $_POST['_wpnonce'] ) && |
| 303 | wp_verify_nonce( $_POST['_wpnonce'], 'bypass-protect' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WP Core doesn't unstrip or sanitize nonces either. |
| 304 | ) { |
| 305 | $this->process_recovery_email(); |
| 306 | |
| 307 | return; |
| 308 | } |
| 309 | |
| 310 | if ( isset( $_GET['loggedout'] ) && 'true' === $_GET['loggedout'] ) { |
| 311 | $this->protect_die( __( 'You successfully logged out.', 'jetpack-waf' ) ); |
| 312 | } |
| 313 | |
| 314 | $this->render_recovery_form(); |
| 315 | } |
| 316 | |
| 317 | /** |
| 318 | * Render the blocked login message. |
| 319 | */ |
| 320 | public function render_blocked_login_message() { |
| 321 | $this->protect_die( $this->get_html_blocked_login_message() ); |
| 322 | } |
| 323 | |
| 324 | /** |
| 325 | * Process sending a recovery email. |
| 326 | */ |
| 327 | public function process_recovery_email() { |
| 328 | $sent = $this->send_recovery_email(); |
| 329 | $show_recovery_form = true; |
| 330 | if ( is_wp_error( $sent ) ) { |
| 331 | if ( 'email_already_sent' === $sent->get_error_code() ) { |
| 332 | $show_recovery_form = false; |
| 333 | } |
| 334 | $this->protect_die( $sent, null, true, $show_recovery_form ); |
| 335 | } else { |
| 336 | $this->render_recovery_success(); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | /** |
| 341 | * Send the recovery form. |
| 342 | */ |
| 343 | public function send_recovery_email() { |
| 344 | $email = isset( $_POST['email'] ) ? wp_unslash( $_POST['email'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- only triggered after bypass-protect nonce check is done, and sanitization is checked on the next line. |
| 345 | if ( sanitize_email( $email ) !== $email || ! is_email( $email ) ) { |
| 346 | return new WP_Error( 'invalid_email', __( "Oops, looks like that's not the right email address. Please try again!", 'jetpack-waf' ) ); |
| 347 | } |
| 348 | $user = get_user_by( 'email', trim( $email ) ); |
| 349 | |
| 350 | if ( ! $user ) { |
| 351 | return new WP_Error( 'invalid_user', __( "Oops, we couldn't find a user with that email. Please try again!", 'jetpack-waf' ) ); |
| 352 | } |
| 353 | $this->email_address = $email; |
| 354 | $path = sprintf( '/sites/%d/protect/recovery/request', Jetpack_Options::get_option( 'id' ) ); |
| 355 | |
| 356 | $response = Client::wpcom_json_api_request_as_blog( |
| 357 | $path, |
| 358 | '1.1', |
| 359 | array( |
| 360 | 'method' => 'post', |
| 361 | ), |
| 362 | array( |
| 363 | 'user_id' => $user->ID, |
| 364 | 'ip' => $this->ip_address, |
| 365 | ) |
| 366 | ); |
| 367 | |
| 368 | $code = wp_remote_retrieve_response_code( $response ); |
| 369 | $result = json_decode( wp_remote_retrieve_body( $response ) ); |
| 370 | |
| 371 | if ( self::HTTP_STATUS_CODE_TOO_MANY_REQUESTS === $code ) { |
| 372 | // translators: email address the recovery instructions were sent to. |
| 373 | return new WP_Error( 'email_already_sent', sprintf( __( 'Recovery instructions were sent to %s. Check your inbox!', 'jetpack-waf' ), esc_html( $this->email_address ) ) ); |
| 374 | } elseif ( is_wp_error( $result ) || empty( $result ) || isset( $result->error ) ) { |
| 375 | return new WP_Error( 'email_send_error', __( 'Oops, we were unable to send a recovery email. Try again.', 'jetpack-waf' ) ); |
| 376 | } |
| 377 | |
| 378 | return true; |
| 379 | } |
| 380 | |
| 381 | /** |
| 382 | * Prevent login by locking the login page. |
| 383 | * |
| 384 | * @param string|WP_Error $content - the content of the page. |
| 385 | * @param string|null $title - the page title. |
| 386 | * @param bool $back_link - the back link. |
| 387 | * @param bool $recovery_form - the recovery form. |
| 388 | */ |
| 389 | public function protect_die( $content, $title = null, $back_link = false, $recovery_form = false ) { |
| 390 | if ( empty( $title ) ) { |
| 391 | $title = __( 'Jetpack has locked your site\'s login page.', 'jetpack-waf' ); |
| 392 | } |
| 393 | if ( is_wp_error( $content ) ) { |
| 394 | $svg = '<svg class="gridicon gridicons-notice" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-2h2v2zm0-4h-2l-.5-6h3l-.5 6z"/></g></svg>'; |
| 395 | $content = '<span class="error"> ' . $svg . $content->get_error_message() . '</span>'; |
| 396 | } |
| 397 | $content = '<p>' . $content . '</p>'; |
| 398 | |
| 399 | // If for some reason the login pop up box show up in the wp-admin. |
| 400 | if ( isset( $_GET['interim-login'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes to the site itself, just rendering an error message. |
| 401 | $content = '<style>html{ background-color: #fff; } #error-message { margin:0 auto; padding: 1em; box-shadow: none; } </style>' . $content; |
| 402 | } |
| 403 | $this->display_page( $title, $content, $back_link, $recovery_form ); |
| 404 | } |
| 405 | |
| 406 | /** |
| 407 | * Render the recovery form. |
| 408 | */ |
| 409 | public function render_recovery_form() { |
| 410 | $content = $this->get_html_blocked_login_message(); |
| 411 | $this->protect_die( $content, null, false, true ); |
| 412 | } |
| 413 | |
| 414 | /** |
| 415 | * Render the recovery instructions. |
| 416 | */ |
| 417 | public function render_recovery_success() { |
| 418 | // translators: the email address the recovery email was sent to. |
| 419 | $this->protect_die( sprintf( __( 'Recovery instructions were sent to %s. Check your inbox!', 'jetpack-waf' ), $this->email_address ) ); |
| 420 | } |
| 421 | |
| 422 | /** |
| 423 | * Get the HTML for the blocked login message. |
| 424 | */ |
| 425 | public function get_html_blocked_login_message() { |
| 426 | $icon = '<svg class="gridicon gridicons-spam" style="fill:#d94f4f" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M17 2H7L2 7v10l5 5h10l5-5V7l-5-5zm-4 15h-2v-2h2v2zm0-4h-2l-.5-6h3l-.5 6z"/></g></svg>'; |
| 427 | $ip = str_replace( 'http://', '', esc_url( 'http://' . $this->ip_address ) ); |
| 428 | return sprintf( |
| 429 | // translators: the IP address that was flagged. |
| 430 | __( '<p>Your IP address <code>%2$s</code> has been flagged for potential security violations. You can unlock your login by sending yourself a special link via email. <a href="%3$s">Learn More</a></p>', 'jetpack-waf' ), // phpcs:ignore WordPress.WP.I18n.NoHtmlWrappedStrings |
| 431 | $icon, |
| 432 | $ip, |
| 433 | esc_url( $this->get_help_url() ) |
| 434 | ); |
| 435 | } |
| 436 | |
| 437 | /** |
| 438 | * Get the HTML recovery form. |
| 439 | */ |
| 440 | public function get_html_recovery_form() { |
| 441 | ob_start(); ?> |
| 442 | <div> |
| 443 | <form method="post" action="?jetpack-protect-recovery=true"> |
| 444 | <?php wp_nonce_field( 'bypass-protect' ); ?> |
| 445 | <p><label for="email"><?php esc_html_e( 'Your email', 'jetpack-waf' ); ?><br/></label> |
| 446 | <input type="email" name="email" class="text-input"/> |
| 447 | <input type="submit" class="button" |
| 448 | value="<?php esc_attr_e( 'Send email', 'jetpack-waf' ); ?>"/> |
| 449 | </p> |
| 450 | </form> |
| 451 | </div> |
| 452 | |
| 453 | <?php |
| 454 | $contents = ob_get_contents(); |
| 455 | ob_end_clean(); |
| 456 | |
| 457 | return $contents; |
| 458 | } |
| 459 | |
| 460 | /** |
| 461 | * Display the page. |
| 462 | * |
| 463 | * @param string $title - the page title. |
| 464 | * @param string $message - the message we're sending. |
| 465 | * @param bool $back_button - the back button. |
| 466 | * @param bool $recovery_form - the recovery form. |
| 467 | * @return never |
| 468 | */ |
| 469 | public function display_page( $title, $message, $back_button = false, $recovery_form = false ) { |
| 470 | |
| 471 | if ( ! headers_sent() ) { |
| 472 | nocache_headers(); |
| 473 | header( 'Content-Type: text/html; charset=utf-8' ); |
| 474 | } |
| 475 | |
| 476 | $text_direction = 'ltr'; |
| 477 | if ( is_rtl() ) { |
| 478 | $text_direction = 'rtl'; |
| 479 | } |
| 480 | ?> |
| 481 | <!DOCTYPE html> |
| 482 | <html xmlns="http://www.w3.org/1999/xhtml" |
| 483 | <?php |
| 484 | if ( function_exists( 'language_attributes' ) && function_exists( 'is_rtl' ) ) { |
| 485 | language_attributes(); |
| 486 | } else { |
| 487 | echo "dir='$text_direction'"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 488 | } |
| 489 | ?> |
| 490 | > |
| 491 | <head> |
| 492 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
| 493 | <meta name="viewport" content="width=device-width"> |
| 494 | <?php |
| 495 | if ( get_option( 'blog_public' ) ) { |
| 496 | echo "<meta name='robots' content='noindex,follow' />\n"; |
| 497 | } else { |
| 498 | echo "<meta name='robots' content='noindex,nofollow' />\n"; |
| 499 | } |
| 500 | ?> |
| 501 | <title><?php echo esc_html( $title ); ?></title> |
| 502 | <style type="text/css"> |
| 503 | html { |
| 504 | background: #f6f6f6; |
| 505 | } |
| 506 | |
| 507 | body { |
| 508 | color: #2e4453; |
| 509 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; |
| 510 | margin: 2em auto; |
| 511 | padding: 1em 2em; |
| 512 | max-width: 460px; |
| 513 | text-align: left; |
| 514 | } |
| 515 | body.is-rtl { |
| 516 | text-align: right; |
| 517 | } |
| 518 | h1 { |
| 519 | clear: both; |
| 520 | color: #3d596d; |
| 521 | font-size: 24px; |
| 522 | margin:0 0 24px 0; |
| 523 | padding: 0; |
| 524 | font-weight: 400; |
| 525 | } |
| 526 | |
| 527 | #error-message { |
| 528 | box-sizing: border-box; |
| 529 | background: white; |
| 530 | box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3; |
| 531 | padding: 24px; |
| 532 | } |
| 533 | |
| 534 | #error-message img { |
| 535 | margin: 0 auto; |
| 536 | display: block; |
| 537 | } |
| 538 | |
| 539 | #error-page { |
| 540 | margin-top: 50px; |
| 541 | } |
| 542 | |
| 543 | #error-page p { |
| 544 | font-size: 14px; |
| 545 | line-height: 1.5; |
| 546 | margin: 24px 0 0; |
| 547 | } |
| 548 | |
| 549 | #error-page code { |
| 550 | font-family: Consolas, Monaco, monospace; |
| 551 | } |
| 552 | |
| 553 | ul li { |
| 554 | margin-bottom: 10px; |
| 555 | font-size: 14px; |
| 556 | } |
| 557 | |
| 558 | a { |
| 559 | color: #00aadc; |
| 560 | } |
| 561 | |
| 562 | label { |
| 563 | font-weight: bold; |
| 564 | font-size:16px; |
| 565 | } |
| 566 | |
| 567 | a:hover, |
| 568 | a:active { |
| 569 | color: #0085be; |
| 570 | } |
| 571 | |
| 572 | a:focus { |
| 573 | color: #124964; |
| 574 | -webkit-box-shadow: 0 0 0 1px #4f94d4, |
| 575 | 0 0 2px 1px rgba(30, 140, 190, .8); |
| 576 | box-shadow: 0 0 0 1px #4f94d4, |
| 577 | 0 0 2px 1px rgba(30, 140, 190, .8); |
| 578 | outline: none; |
| 579 | } |
| 580 | |
| 581 | .button { |
| 582 | background: #00aadc; |
| 583 | color: white; |
| 584 | border-color: #008ab3; |
| 585 | border-style: solid; |
| 586 | border-width: 1px 1px 2px; |
| 587 | cursor: pointer; |
| 588 | display: inline-block; |
| 589 | margin: 0; |
| 590 | margin-right: 0; |
| 591 | outline: 0; |
| 592 | overflow: hidden; |
| 593 | font-weight: 500; |
| 594 | text-overflow: ellipsis; |
| 595 | text-decoration: none; |
| 596 | vertical-align: top; |
| 597 | box-sizing: border-box; |
| 598 | font-size: 14px; |
| 599 | line-height: 21px; |
| 600 | border-radius: 4px; |
| 601 | padding: 7px 14px 9px; |
| 602 | -webkit-appearance: none; |
| 603 | -moz-appearance: none; |
| 604 | appearance: none; |
| 605 | font-size: 14px; |
| 606 | width: 100%; |
| 607 | } |
| 608 | |
| 609 | .button:hover, |
| 610 | .button:focus { |
| 611 | border-color: #005082; |
| 612 | outline: none; |
| 613 | } |
| 614 | |
| 615 | .button:focus { |
| 616 | border-color: #005082; |
| 617 | -webkit-box-shadow: 0 0 3px rgba(0, 115, 170, .8); |
| 618 | box-shadow: 0 0 3px rgba(0, 115, 170, .8); |
| 619 | outline: none; |
| 620 | } |
| 621 | .button::-moz-focus-inner { |
| 622 | border: 0; |
| 623 | } |
| 624 | |
| 625 | .button:active { |
| 626 | border-width: 2px 1px 1px; |
| 627 | } |
| 628 | .gridicon { |
| 629 | fill: currentColor; |
| 630 | vertical-align: middle; |
| 631 | } |
| 632 | #error-footer { |
| 633 | padding: 16px; |
| 634 | } |
| 635 | #error-footer a { |
| 636 | text-decoration: none; |
| 637 | line-height:20px; |
| 638 | font-size: 14px; |
| 639 | color: #4f748e; |
| 640 | } |
| 641 | #error-footer a:hover { |
| 642 | color: #2e4453; |
| 643 | } |
| 644 | #error-footer .gridicon{ |
| 645 | width: 16px; |
| 646 | } |
| 647 | #error-footer .gridicons-help { |
| 648 | width: 24px; |
| 649 | margin-right:8px; |
| 650 | } |
| 651 | |
| 652 | .is-rtl #error-footer .gridicons-help { |
| 653 | margin-left:8px; |
| 654 | } |
| 655 | |
| 656 | .error { |
| 657 | background: #d94f4f; |
| 658 | color:#FFF; |
| 659 | display: block; |
| 660 | border-radius: 3px; |
| 661 | line-height: 1.5; |
| 662 | padding: 16px; |
| 663 | padding-left: 42px; |
| 664 | } |
| 665 | .is-rtl .error { |
| 666 | padding-right: 42px; |
| 667 | } |
| 668 | .error .gridicon { |
| 669 | float: left; |
| 670 | margin-left: -32px; |
| 671 | } |
| 672 | |
| 673 | .is-rtl .error .gridicon { |
| 674 | float: right; |
| 675 | margin-right: -32px; |
| 676 | } |
| 677 | |
| 678 | .text-input { |
| 679 | margin: 0; |
| 680 | padding: 7px 14px; |
| 681 | width: 100%; |
| 682 | color: #2e4453; |
| 683 | font-size: 16px; |
| 684 | line-height: 1.5; |
| 685 | border: 1px solid #c8d7e1; |
| 686 | background-color: white; |
| 687 | transition: all .15s ease-in-out; |
| 688 | box-sizing: border-box; |
| 689 | margin: 8px 0 16px; |
| 690 | } |
| 691 | #image { |
| 692 | display: block; |
| 693 | width: 200px; |
| 694 | margin: 0 auto; |
| 695 | } |
| 696 | <?php |
| 697 | $rtl_class = ''; |
| 698 | if ( 'rtl' === $text_direction ) { |
| 699 | $rtl_class = 'class="is-rtl"'; |
| 700 | echo 'body { font-family: Tahoma, Arial; }'; |
| 701 | } |
| 702 | ?> |
| 703 | </style> |
| 704 | </head> |
| 705 | <body id="error-page" <?php echo $rtl_class; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> |
| 706 | <h1 id="error-title"><?php echo esc_html( $title ); ?></h1> |
| 707 | <div id="error-message"> |
| 708 | <svg id="image" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 250 134"> |
| 709 | <path fill="#E9EFF4" d="M205.2,129.8c3.7-0.7,7.4-0.9,11.1-1.1l5.5-0.1l5.5,0c3.7,0,7.4,0.1,11.1,0.2c3.7,0.1,7.4,0.3,11.1,0.8 c0.3,0,0.5,0.3,0.5,0.6c0,0.2-0.2,0.4-0.5,0.5c-3.7,0.5-7.4,0.6-11.1,0.8c-3.7,0.1-7.4,0.2-11.1,0.2l-5.5,0l-5.5-0.1 c-3.7-0.1-7.4-0.4-11.1-1.1c-0.1,0-0.2-0.2-0.2-0.3C205,129.9,205.1,129.8,205.2,129.8"/> |
| 710 | <path fill="#E9EFF4" d="M0.2,130.9c3-0.7,5.9-0.9,8.9-1.1l4.4-0.1l4.4,0c3,0,5.9,0.1,8.9,0.2c3,0.1,5.9,0.3,8.9,0.8 c0.3,0,0.5,0.3,0.4,0.6c0,0.2-0.2,0.4-0.4,0.4c-3,0.5-5.9,0.6-8.9,0.8c-3,0.1-5.9,0.2-8.9,0.2l-4.4,0l-4.4-0.1 c-3-0.1-5.9-0.4-8.9-1.1c-0.1,0-0.2-0.2-0.2-0.3C0,131,0.1,130.9,0.2,130.9"/> |
| 711 | <path fill="#C8D7E2" d="M101.6,130.1H70.1V52.5c0-8.5,6.9-15.3,15.3-15.3h16.1V130.1z"/> |
| 712 | <path fill="#0DA9DD" d="M191.5,130.1h-73.8v-5.4c0-8.9,7.2-16.1,16.1-16.1h57.7V130.1z"/> |
| 713 | <path fill="#C7E9F5" d="M55.2,25.6l-0.1,9.8L55,57l-0.1,21.6c0,0.2,0.2,0.4,0.4,0.4c0.2,0,0.4-0.2,0.4-0.4L56.6,57l0.8-21.6 c0.1-3.3,0.2-6.5,0.3-9.8H55.2z"/> |
| 714 | <path fill="#C7E9F5" d="M203.1,25.6l0.1,18.1c0.2,28.8,0.4,57.6,1.2,86.3c0,0.4,0.4,0.8,0.8,0.8c0.4,0,0.8-0.3,0.8-0.8 c0.8-28.8,1-57.6,1.2-86.3l0.1-18.1H203.1z"/> |
| 715 | <path fill="#7FD3F2" d="M55.3,25.6v-8.2v-6.8c0-5.9,4-10.7,9-10.7h134c5,0,9,4.8,9,10.7v14.9H55.3z"/> |
| 716 | <path fill="#005083" d="M210.7,25.6c-13.3,1.1-26.7,1-40,1l-40,0.2l-40-0.2c-13.3-0.1-26.7,0-40-1V25c13.3-1.1,26.7-1,40-1l40-0.2 l40,0.2c13.3,0.1,26.7,0,40,1V25.6z"/> |
| 717 | <polygon fill="#C7E9F5" points="168.7,95.6 117.7,95.6 117.7,44.6 "/> |
| 718 | <path fill="#C8D7E2" d="M191.5,56.5c0,11-8.9,19.9-19.9,19.9c-11,0-19.9-8.9-19.9-19.9c0-11,8.9-19.9,19.9-19.9 C182.6,36.6,191.5,45.5,191.5,56.5"/> |
| 719 | <path fill="#FFFFFF" d="M213.2,95.5c-3.3-5.1-3.2-16.7-3.2-28.4h-32.3c0,0-5.2,25.5,4.6,33c7.5-0.1,29.9-0.6,29.9-0.6"/> |
| 720 | <path fill="#C8D7E2" d="M213.5,95.3l-0.1-0.1l-0.3-0.5c-0.2-0.4-0.3-0.7-0.5-1.1c-0.3-0.8-0.5-1.6-0.7-2.4c-0.1-0.5-0.2-1.1-0.3-1.6 c-0.4,0-0.8,0-1.2,0c0.5,2.1,1.1,4.3,2.4,6.1l0.2,0.2c0.2,0,0.4-0.1,0.5-0.3C213.6,95.5,213.6,95.4,213.5,95.3L213.5,95.3z"/> |
| 721 | <path fill="#C8D7E2" d="M212.5,98.6c-0.1,0-0.2,0-0.3,0l-0.1,0H212l-0.3,0l-0.6,0l-1.3,0l-2.5,0l-5,0l-19.5,0.2 c-1.9-1.7-3.1-4.1-3.8-6.5c-0.8-2.6-1.1-5.4-1.2-8.2c-0.2-5.2,0.3-10.4,1.1-15.6l5.7-0.1c0-0.9,0-1.8,0-2.6l-4.4,0l-2.5,0 c-0.4,0-0.8,0.2-1,0.5c-0.1,0.2-0.2,0.3-0.3,0.5l-0.1,0.3l-0.2,1.2c-0.3,1.7-0.5,3.3-0.7,5c-0.3,3.3-0.5,6.7-0.4,10.1 c0.1,3.4,0.5,6.7,1.5,10c0.5,1.6,1.2,3.2,2.2,4.7c0.5,0.7,1,1.4,1.7,2c0.3,0.3,0.6,0.6,1,0.9l0.1,0.1c0.1,0,0.2,0.1,0.3,0.2 c0.2,0.1,0.5,0.1,0.6,0.1l0.6,0l20-0.6l5-0.2l2.5-0.1l1.2,0l0.3,0l0.2,0c0,0,0.3,0,0.4-0.1c0.3-0.2,0.5-0.5,0.5-0.9 C213.1,99.1,212.9,98.7,212.5,98.6z"/> |
| 722 | <path fill="#FFFFFF" d="M223.1,84.8c-3.3-5.1-4.8-16.7-4.8-28.4h-32.3c0,0-3.5,25.5,6.3,33c7.5-0.1,29.9-0.6,29.9-0.6"/> |
| 723 | <path fill="#C8D7E2" d="M222.9,84.9c-1.3-2.1-2.2-4.4-2.8-6.7c-0.6-2.4-1.1-4.8-1.5-7.2c-0.7-4.8-1-9.1-1-13.9l0,0l-31,0.1l0,0 c-0.4,2.8-0.5,5.1-0.5,7.9c-0.1,2.9,0,5.7,0.3,8.6c0.3,2.8,0.8,5.7,1.7,8.3c0.9,2.6,2.3,5.2,4.5,6.9l-0.4-0.1l14.9-0.2 c5-0.1,10-0.1,14.9-0.1c0.1,0,0.3,0.1,0.3,0.3c0,0.1-0.1,0.3-0.2,0.3c-5,0.2-10,0.4-14.9,0.5l-14.9,0.4c-0.1,0-0.3,0-0.4-0.1l0,0 c-2.5-1.9-3.9-4.7-5-7.4c-1-2.8-1.5-5.7-1.9-8.6c-0.3-2.9-0.4-5.8-0.4-8.8c0.1-2.9,0.2-5.8,0.6-8.8c0-0.4,0.4-0.6,0.7-0.6h0 l32.3,0.1h0c0.3,0,0.6,0.3,0.6,0.6v0c0,4.8,0.2,9.6,0.7,14.4c0.3,2.4,0.6,4.8,1.2,7.1c0.5,2.3,1.2,4.7,2.4,6.8c0,0.1,0,0.1,0,0.2 C223.1,85,223,85,222.9,84.9"/> |
| 724 | <path fill="#C8D7E2" d="M192.1,67.1c1.6-0.9,3.4-1.2,5.1-1.3c1.7-0.2,3.5-0.2,5.2-0.2c3.5,0.1,6.9,0.2,10.3,1c0.1,0,0.2,0.2,0.2,0.3 c0,0.1-0.1,0.2-0.2,0.2c-3.4,0.2-6.9,0-10.3,0c-1.7,0-3.4,0-5.1,0c-1.7,0-3.4,0.1-5.1,0.3l0,0c-0.1,0-0.1,0-0.1-0.1 C192,67.2,192.1,67.1,192.1,67.1"/> |
| 725 | <path fill="#C8D7E2" d="M194.1,74c1.4,0,2.7,0,4.1,0c1.4,0,2.7,0,4.1,0c2.7,0,5.4-0.1,8.2-0.2c0.1,0,0.3,0.1,0.3,0.3 c0,0.1-0.1,0.2-0.2,0.3c-1.3,0.5-2.7,0.7-4.1,0.9c-1.4,0.2-2.8,0.2-4.2,0.3c-1.4,0-2.8,0-4.2-0.2c-1.4-0.2-2.8-0.4-4.1-1.1 c-0.1,0-0.1-0.1,0-0.2C193.9,74.1,194,74,194.1,74L194.1,74z"/> |
| 726 | <path fill="#86A6BD" d="M40.2,88.6c-0.5,0-0.8-0.4-0.9-0.9l-0.1-8.2c0-0.7,0-1.4,0-2.1c0.1-0.7,0.2-1.5,0.4-2.2c0.4-1.4,1-2.8,1.9-4 c1.7-2.5,4.3-4.3,7.1-5.1c0.7-0.2,1.5-0.3,2.2-0.5c0.7-0.1,1.5-0.1,2.2-0.1c1.3,0,2.9,0,4.4,0.4c2.9,0.7,5.6,2.5,7.4,4.9 c0.9,1.2,1.6,2.6,2.1,4c0.5,1.4,0.6,3,0.6,4.4l0,16.4c0,0.7-0.6,1.3-1.3,1.3l-6.7,0c-0.7,0-1.3-0.6-1.3-1.3v0l0-10.8l0-5.4 c0-1.4-0.7-2.8-1.8-3.5c-0.6-0.4-1.3-0.6-2-0.7c-0.7,0-1.9,0-2.5,0c-1.4,0.1-2.7,1-3.3,2.3c-0.3,0.7-0.4,1.3-0.4,2.1l0,2.7 l-0.1,5.4l0,0c0,0.5-0.4,0.9-1,0.9"/> |
| 727 | <path fill="#FFFFFF" d="M41.1,86.9l0.1-7.3c-0.1-2.6,0.7-5,2.1-7.1c1.4-2,3.6-3.5,5.9-4.1c0.6-0.2,1.2-0.3,1.8-0.3 c0.6,0,1.2-0.1,1.9,0c1.4,0,2.5,0,3.7,0.4c2.4,0.6,4.5,2,5.9,4c0.7,1,1.3,2.1,1.6,3.2c0.4,1.2,0.5,2.3,0.5,3.7l0,15.1l0,0l-4.2,0 l0-9.5l0-5.4c0-2.2-1.2-4.4-3-5.5c-0.9-0.6-2-0.9-3.1-1c-1.1,0-1.7,0-2.9,0c-2.2,0.2-4.2,1.7-5.1,3.6c-0.5,0.9-0.7,2.1-0.6,3.1 l0,2.7l0.1,4.4l0,0L41.1,86.9L41.1,86.9"/> |
| 728 | <path fill="#86A6BD" d="M36.3,133c-1.9,0-3.8-1.1-4.8-2.8c-0.5-0.8-0.7-1.8-0.7-2.8l0-2.4l0-9.6l-0.1-9.6l0-4.8c0-0.7,0-1.8,0.3-2.8 c0.3-1,0.9-1.8,1.7-2.5c0.8-0.6,1.7-1.1,2.7-1.3c1.1-0.2,1.8-0.1,2.6-0.1l4.8,0l9.6-0.1l19.2,0c2.1,0,4.1,1.2,5.1,3 c0.5,0.9,0.8,2,0.8,3l0,2.4l0,9.6l-0.1,9.6l0,4.8c0,0.7,0,1.8-0.4,2.8c-0.3,0.9-1,1.8-1.7,2.4c-0.8,0.6-1.7,1.1-2.7,1.2 c-1.1,0.1-1.8,0-2.6,0.1l-4.8,0l-9.6-0.1L36.3,133z"/> |
| 729 | <path fill="#FFFFFF" d="M74.8,112.3l-0.1-9.6l0-2.4c0-0.6-0.1-1.1-0.4-1.6c-0.6-1-1.7-1.6-2.8-1.6l-19.2,0L42.7,97l-4.8,0 c-0.8,0-1.7,0-2.2,0c-0.6,0.1-1.1,0.3-1.6,0.7c-0.5,0.4-0.8,0.9-1,1.4c-0.2,0.6-0.2,1.1-0.2,2l0,4.8l-0.1,9.6l0,9.6l0,2.4 c0,0.6,0.2,1.3,0.5,1.8c0.6,1.1,1.9,1.8,3.1,1.8l19.2-0.1l9.6-0.1l4.8,0c0.8,0,1.7,0,2.2-0.1c0.6-0.1,1.2-0.4,1.6-0.8 c0.5-0.4,0.8-0.9,1-1.5c0.2-0.6,0.2-1.1,0.2-2l0-4.8L74.8,112.3z"/> |
| 730 | <path fill="#86A6BD" d="M48.1,121.4l2.9-6.2c0.3-0.6,0.2-1.3-0.3-1.8c-1-1-1.5-2.5-1.2-4c0.3-1.7,1.7-3.1,3.4-3.4 c2.9-0.6,5.4,1.6,5.4,4.4c0,1.2-0.5,2.3-1.3,3.1c-0.5,0.5-0.6,1.2-0.3,1.8l2.9,6.2c0.1,0.2-0.1,0.5-0.3,0.5H48.4 C48.1,121.9,48,121.6,48.1,121.4"/> |
| 731 | </svg> |
| 732 | |
| 733 | <?php echo $message; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- message includes HTML that's marked up ourselves. ?> |
| 734 | <?php |
| 735 | if ( $recovery_form ) { |
| 736 | echo $this->get_html_recovery_form(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- content is escaped in the function. |
| 737 | } |
| 738 | ?> |
| 739 | </div> |
| 740 | <div id="error-footer"> |
| 741 | <?php |
| 742 | if ( $back_button && ! $recovery_form ) { |
| 743 | if ( 'rtl' === $text_direction ) { |
| 744 | $back_button_icon = '<svg class="gridicon gridicons-arrow-right" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8-8-8z"/></g></svg>'; |
| 745 | } else { |
| 746 | $back_button_icon = '<svg class="gridicon gridicons-arrow-left" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></g></svg>'; |
| 747 | } |
| 748 | ?> |
| 749 | <a href='javascript:history.back()' |
| 750 | <?php |
| 751 | printf( |
| 752 | /* translators: %s is HTML markup, for a back icon. */ |
| 753 | __( '%s Back', 'jetpack-waf' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 754 | $back_button_icon // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 755 | ); |
| 756 | ?> |
| 757 | </a> |
| 758 | <?php |
| 759 | } else { |
| 760 | $help_icon = '<svg class="gridicon gridicons-help" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 16h-2v-2h2v2zm0-4.14V15h-2v-2c0-.552.448-1 1-1 1.103 0 2-.897 2-2s-.897-2-2-2-2 .897-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.862-1.278 3.413-3 3.86z"/></g></svg>'; |
| 761 | ?> |
| 762 | <a href="<?php echo esc_url( $this->get_help_url() ); ?>" rel="noopener noreferrer" target="_blank"> |
| 763 | <?php |
| 764 | printf( |
| 765 | /* translators: %s is HTML markup, for a help icon. */ |
| 766 | __( '%s Get help unlocking your site', 'jetpack-waf' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 767 | $help_icon // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 768 | ); |
| 769 | ?> |
| 770 | </a> |
| 771 | <?php } ?> |
| 772 | </div> |
| 773 | </body> |
| 774 | </html> |
| 775 | <?php |
| 776 | die( 0 ); |
| 777 | } |
| 778 | } |