class-redux-connection-banner.php (20181B)
1 <?php 2 /** 3 * Redux Connection Banner Class 4 * 5 * @class Redux_Core 6 * @version 4.0.0 7 * @package Redux Framework 8 */ 9 10 // @codingStandardsIgnoreStart 11 if ( ! defined( 'ABSPATH' ) ) { 12 exit; 13 } 14 15 if ( ! class_exists( 'Redux_Connection_Banner', false ) ) { 16 17 /** 18 * Class Redux_Connection_Banner 19 */ 20 class Redux_Connection_Banner { 21 /** 22 * Plugin version, used for cache-busting of style and script file references. 23 * 24 * @since 1.0.0 25 * @var string 26 */ 27 protected $version = '1.0.0'; 28 29 /** 30 * Singleton instance. 31 * 32 * @var Redux_Connection_Banner 33 **/ 34 private static $instance = null; 35 36 /** 37 * Register option. 38 * 39 * @var string $register_option 40 */ 41 private $register_option = 'redux-connection-register'; 42 /** 43 * Dismiss option. 44 * 45 * @var string $dismiss_option 46 */ 47 private $dismiss_option = 'redux-connection-dismiss'; 48 49 /** 50 * Nonce slug. 51 * 52 * @var string $dismiss_options 53 */ 54 private $nonce = 'redux-connection-nonce'; 55 56 /** 57 * URLs. 58 * 59 * @var array $urls 60 */ 61 private $urls = array(); 62 63 /** 64 * Init function. 65 * 66 * @return Redux_Connection_Banner 67 */ 68 public static function init(): ?Redux_Connection_Banner { 69 if ( is_null( self::$instance ) ) { 70 self::$instance = new Redux_Connection_Banner(); 71 } 72 73 return self::$instance; 74 } 75 76 /** 77 * Redux_Connection_Banner constructor. 78 * 79 * Since we call the Redux_Connection_Banner:init() method from the `Redux` class, and after 80 * the admin_init action fires, we know that the admin is initialized at this point. 81 */ 82 private function __construct() { 83 $clean_get = $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended 84 if ( isset( $clean_get['_wpnonce'] ) && wp_verify_nonce( $clean_get['_wpnonce'], $this->nonce ) ) { 85 if ( isset( $clean_get[ $this->register_option ] ) ) { 86 Redux_Functions_Ex::set_activated( $clean_get[ $this->register_option ] ); 87 return; 88 } 89 if ( isset( $clean_get[ $this->dismiss_option ] ) ) { 90 Redux_Functions_Ex::set_deactivated(); 91 update_option( 'redux-framework_tracking_notice', 'hide' ); 92 return; 93 } 94 } 95 96 add_action( 'wp_ajax_redux_activation', array( $this, 'admin_ajax' ) ); // Executed when logged in. 97 add_action( 'current_screen', array( $this, 'maybe_initialize_hooks' ) ); 98 } 99 100 /** 101 * Get the URL for the current page to fallback if JS is broken. 102 * 103 * @param bool|string $location Used to determine the location of the banner for account creation. 104 * @since 4.1.21 105 * @return array 106 */ 107 private function get_urls( $location = true ): array { 108 if ( ! empty( $this->urls ) ) { 109 return $this->urls; 110 } 111 112 global $pagenow; 113 114 $clean_get = $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended 115 if ( isset( $clean_get[ $this->register_option ] ) ) { 116 unset( $clean_get[ $this->register_option ] ); 117 } 118 if ( isset( $clean_get[ $this->dismiss_option ] ) ) { 119 unset( $clean_get[ $this->dismiss_option ] ); 120 } 121 $base_url = admin_url( add_query_arg( $clean_get, $pagenow ) ); 122 123 return array( 124 'dismiss' => wp_nonce_url( add_query_arg( $this->dismiss_option, true, $base_url ), $this->nonce ), 125 'register' => wp_nonce_url( add_query_arg( $this->register_option, $location, $base_url ), $this->nonce ), 126 ); 127 } 128 129 /** 130 * AJAX callback for dismissing the notice. 131 */ 132 public function admin_ajax() { 133 // phpcs:ignore WordPress.Security.NonceVerification.Recommended 134 $nonce = isset( $_REQUEST['nonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) ) : ''; 135 if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, $this->nonce ) ) { 136 die( __( 'Security check failed.', 'redux-framework' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 137 } 138 if ( 'true' === $_REQUEST['activate'] ) { 139 Redux_Functions_Ex::set_activated( sanitize_text_field( wp_unslash( $_REQUEST['activate'] ) ) ); 140 } else { 141 Redux_Functions_Ex::set_deactivated(); 142 update_option( 'redux-framework_tracking_notice', 'hide' ); 143 } 144 die(); 145 } 146 147 /** 148 * AJAX callback for dismissing the notice. 149 * 150 * @param string|null $admin_body_class Class string. 151 * 152 * @return string 153 */ 154 public function admin_body_class( ?string $admin_body_class = '' ): string { 155 $classes = explode( ' ', trim( $admin_body_class ) ); 156 157 $classes[] = false ? 'redux-connected' : 'redux-disconnected'; 158 159 $admin_body_class = implode( ' ', array_unique( $classes ) ); 160 return " $admin_body_class "; 161 } 162 163 /** 164 * Will initialize hooks to display the new (as of 4.4) connection banner if the current user can 165 * connect Redux, if Redux has not been deactivated, and if the current page is the plugins page. 166 * 167 * This method should not be called if the site is connected to WordPress.com or if the site is in development mode. 168 * 169 * @since 4.4.0 170 * @since 4.5.0 Made the new (as of 4.4) connection banner display to everyone by default. 171 * @since 5.3.0 Running another split test between 4.4 banner and a new one in 5.3. 172 * @since 7.2 B test was removed. 173 * 174 * @param $current_screen 175 */ 176 public function maybe_initialize_hooks( $current_screen ) { 177 // Redux_Functions_Ex::set_deactivated(); // Test code. 178 179 if ( Redux_Functions_Ex::activated() || 'hide' === get_option( 'redux-framework_tracking_notice', null ) ) { 180 return; 181 } 182 183 // Don't show the connect notice anywhere but the plugins.php after activating 184 if ( 'plugins' !== $current_screen->base && 'dashboard' !== $current_screen->base ) { 185 add_action( 'redux_admin_notices_run', array( $this, 'panel_admin_notice' ), 100, 2 ); 186 add_action( 'admin_head', array( $this, 'admin_head' ) ); 187 return; 188 } 189 190 // Only show this notice when the plugin is installed. 191 if ( class_exists( 'Redux_Framework_Plugin' ) && false === Redux_Framework_Plugin::$crash ) { 192 add_action( 'admin_notices', array( $this, 'render_banner' ) ); 193 add_action( 'network_admin_notices', array( $this, 'network_connect_notice' ) ); 194 add_action( 'admin_head', array( $this, 'admin_head' ) ); 195 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 ); 196 197 // Only fires immediately after plugin activation 198 if ( get_transient( 'activated_Redux' ) ) { 199 add_action( 'admin_notices', array( $this, 'render_connect_prompt_full_screen' ) ); 200 delete_transient( 'activated_Redux' ); 201 } 202 } 203 } 204 205 /** 206 * Enqueues JavaScript and CSS for new connect-in-place flow. 207 * 208 * @since 7.7 209 */ 210 public static function enqueue_connect_button_scripts() { 211 global $is_safari; 212 213 wp_enqueue_script( 214 'Redux-connect-button', 215 '_inc/connect-button.js', 216 array( 'jquery' ), 217 Redux_Core::$version, 218 true 219 ); 220 221 wp_enqueue_style( 222 'Redux-connect-button', 223 'css/Redux-connect.css' 224 ); 225 226 $ReduxApiUrl = wp_parse_url( Redux::connection()->api_url( '' ) ); 227 228 // Due to the limitation in how 3rd party cookies are handled in Safari, 229 // we're falling back to the original flow on Safari desktop and mobile. 230 if ( $is_safari ) { 231 $force_variation = 'original'; 232 } elseif ( Constants::is_true( 'Redux_SHOULD_USE_CONNECTION_IFRAME' ) ) { 233 $force_variation = 'in_place'; 234 } elseif ( Constants::is_defined( 'Redux_SHOULD_USE_CONNECTION_IFRAME' ) ) { 235 $force_variation = 'original'; 236 } else { 237 $force_variation = null; 238 } 239 240 $tracking = new Automattic\Redux\Tracking(); 241 $identity = $tracking->tracks_get_identity( get_current_user_id() ); 242 243 wp_localize_script( 244 'Redux-connect-button', 245 'reduxConnect', 246 array( 247 'apiBaseUrl' => esc_url_raw( rest_url( 'Redux/v4' ) ), 248 'registrationNonce' => wp_create_nonce( 'Redux-registration-nonce' ), 249 'apiNonce' => wp_create_nonce( 'wp_rest' ), 250 'apiSiteDataNonce' => wp_create_nonce( 'wp_rest' ), 251 'buttonTextRegistering' => __( 'Loading...', 'redux-framework' ), 252 'ReduxApiDomain' => $ReduxApiUrl['scheme'] . '://' . $ReduxApiUrl['host'], 253 'forceVariation' => $force_variation, 254 'connectInPlaceUrl' => Redux::admin_url( 'page=Redux#/setup' ), 255 'dashboardUrl' => Redux::admin_url( 'page=Redux#/dashboard' ), 256 'plansPromptUrl' => Redux::admin_url( 'page=Redux#/plans-prompt' ), 257 'identity' => $identity, 258 'preFetchScript' => plugins_url( '_inc/build/admin.js', Redux__PLUGIN_FILE ) . '?ver=' . Redux__VERSION, 259 ) 260 ); 261 } 262 263 /** 264 * Display the admin notice to users that have not opted-in or out 265 * 266 * @return void 267 */ 268 public function panel_admin_notice( $args ) { 269 270 $urls = $this->get_urls( 'panel_banner' ); 271 272 $this->client = Redux_Core::$appsero; 273 // don't show tracking if a local server 274 275 if ( empty( $this->notice ) ) { 276 $name = 'Redux'; 277 // if ( isset( $args['display_name'] ) && !empty( $args['display_name'] )) { 278 // $name = $name . ' & '.$args['display_name']; 279 // } 280 $notice = sprintf( __( 'Register <strong>%1$s</strong> to enable automatic Google Font updates service. Plus unlock all free block templates in the Redux template library.', 'redux-framework' ), $name ); 281 } else { 282 $notice = $this->notice; 283 } 284 285 $notice .= ' (<a class="redux-insights-data-we-collect" href="#" style="white-space: nowrap;">' . __( 'learn more', 'redux-framework' ) . '</a>)'; 286 287 $notice .= '<p class="description" style="display:none;">' . self::tos_blurb( 'option_panel' ) . ' </p>'; 288 289 echo '<div class="updated" id="redux-connect-message" data-nonce="' . wp_create_nonce( $this->nonce ) . '" style="border-left-color: #24b0a6;"><p>'; 290 echo $notice; 291 echo '</p><p class="submit">'; 292 echo ' <a href="' . esc_url( $urls['register'] ) . '" class="button-primary button-large redux-activate-connection redux-connection-banner-action" data-url="' . admin_url( 'admin-ajax.php' ) . '" data-activate="panel_banner">' . __( 'Register Now', 'redux-framework' ) . '</a>'; 293 echo ' <a href="' . esc_url( $urls['dismiss'] ) . '" style="color: #aaa;" class="redux-connection-banner-action" data-activate="false" data-url="' . admin_url( 'admin-ajax.php' ) . '">' . __( 'No thanks', 'redux-framework' ) . '</a>'; 294 echo '</p></div>'; 295 echo '<style>.wp-core-ui .button-primary.redux-activate-connection{background: #24b0a6;}.wp-core-ui .button-primary.redux-activate-connection:hover{background: #19837c;}</style>'; 296 297 echo "<noscript><style>#redux-connect-message{display:none;}</style></noscript>"; 298 299 } 300 301 /** 302 * Hide Individual Dashboard Pages 303 * 304 * @access public 305 * @since 4.0.0 306 * @return void 307 */ 308 public function admin_head() { 309 ?> 310 311 <link 312 rel='stylesheet' id='redux-banner-css' <?php // phpcs:ignore WordPress.WP.EnqueuedResources ?> 313 href='<?php echo esc_url( Redux_Core::$url ); ?>inc/welcome/css/redux-banner.css' 314 type='text/css' media='all'/> 315 <script 316 id="redux-banner-admin-js" 317 src='<?php echo esc_url( Redux_Core::$url ); ?>inc/welcome/js/redux-banner-admin.js'> 318 </script> 319 <?php 320 } 321 322 /** 323 * Renders the new connection banner as of 4.4.0. 324 * 325 * @since 7.2 Copy and visual elements reduced to show the new focus of Redux on Security and Performance. 326 * @since 4.4.0 327 */ 328 public function render_banner() { 329 330 $urls = $this->get_urls( 'main_banner' ); 331 332 ?> 333 <div id="redux-connect-message" class="updated redux-banner-container" data-nonce="<?php echo wp_create_nonce( $this->nonce ); ?>"> 334 <div class="redux-banner-container-top-text"> 335 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="0" fill="none" width="24" height="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> 336 <span> 337 <?php esc_html_e( 'You’re almost done. Register for our service to unlock even more tools to help you build better sites faster in WordPress.', 'redux-framework' ); ?> 338 </span> 339 </div> 340 <div class="redux-banner-inner-container"> 341 <a href="<?php echo esc_url( $urls['dismiss'] ); ?>" data-url="<?php echo admin_url( 'admin-ajax.php' ); ?>" 342 class="notice-dismiss redux-banner-svg-dismiss redux-connection-banner-action" 343 title="<?php esc_attr_e( 'Dismiss this notice', 'redux-framework' ); ?>" data-activate="false"> 344 </a> 345 346 <div class="redux-banner-content-container"> 347 348 <!-- slide 1: intro --> 349 <div class="redux-banner-slide redux-banner-slide-one redux-slide-is-active"> 350 351 <div class="redux-banner-content-icon redux-illo"> 352 <a href="<?php echo esc_url( 'https://redux.io/?utm_source=plugin&utm_medium=appsero&utm_campaign=redux_banner_logo' ); ?>" target="_blank"><img 353 src="<?php echo esc_url( Redux_Core::$url ); ?>assets/img/logo--color.svg" 354 class="redux-banner-content-logo" 355 alt=" 356 <?php 357 esc_attr_e( 358 'Visit our website to learn more!', 359 'redux-framework' 360 ); 361 ?> 362 " 363 /></a> 364 <img 365 src="<?php echo esc_url( Redux_Core::$url ); ?>assets/img/redux-powering-up.svg" 366 class="redux-banner-hide-phone-and-smaller" 367 alt=" 368 <?php 369 esc_attr_e( 370 'Redux helps you to take your site to the next level with tools that greatly enhance your WordPress experience.', 371 'redux-framework' 372 ); 373 ?> 374 " 375 height="auto" 376 /> 377 </div> 378 379 <div class="redux-banner-slide-text"> 380 <h2><?php esc_html_e( 'Build better sites faster with Redux', 'redux-framework' ); ?></h2> 381 <p> 382 <?php 383 esc_html_e( 384 'The Redux block library service allows you to build any site you want in minutes with a click of a button. With over 1,000+ templates, Redux helps you build sites fast!', 385 'redux_framework' 386 ); 387 ?> 388 </p> 389 390 <p><em> 391 <?php 392 esc_html_e( 393 'No registration is required to use Redux as you always have. By registering for our service you gain access to Google Font updates as well as access to all free templates in our block template library.', 394 'redux-framework' 395 ); 396 ?> 397 </em> 398 </p> 399 400 <div class="redux-banner-button-container"> 401 <span class="redux-banner-tos-blurb"><?php echo self::tos_blurb( 'plugin_dashboard' ); ?></span> 402 403 <a href="<?php echo esc_url( $urls['dismiss'] ); ?>" data-url="<?php echo admin_url( 'admin-ajax.php' ); ?>" 404 class="button button-tiny button-link redux-connection-banner-action" 405 title="<?php esc_attr_e( 'No thanks', 'redux-framework' ); ?>" data-activate="false"><?php esc_html_e( 'No Thanks', 'redux-framework' ); ?></a> 406 407 <a href="<?php echo esc_url( $urls['register'] ); ?>" data-url="<?php echo admin_url( 'admin-ajax.php' ); ?>" data-activate="main_banner" 408 class="button button-primary button-large redux-alt-connect-button redux-connection-banner-action"> 409 <?php esc_html_e( 'Register', 'redux-framework' ); ?> 410 </a> 411 </div> 412 413 </div> 414 </div> <!-- end slide 1 --> 415 </div> 416 </div> 417 </div> 418 <noscript><style>#redux-connect-message{display:none;}</style></noscript> 419 <?php 420 } 421 422 /** 423 * Renders the full-screen connection prompt. Only shown once and on plugin activation. 424 */ 425 public static function render_connect_prompt_full_screen() { 426 $current_screen = get_current_screen(); 427 if ( 'plugins' === $current_screen->base ) { 428 $bottom_connect_url_from = 'full-screen-prompt'; 429 } else { 430 $bottom_connect_url_from = 'landing-page-bottom'; 431 } 432 433 if ( 'plugins' === $current_screen->base ) : 434 ?> 435 <div class="redux-banner-full-container"> 436 <div class="redux-banner-full-container-card"> 437 <div class="redux-banner-full-dismiss"> 438 <svg class="redux-banner-svg-dismiss" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>Dismiss Redux Connection Window</title><rect x="0" fill="none" /><g><path d="M17.705 7.705l-1.41-1.41L12 10.59 7.705 6.295l-1.41 1.41L10.59 12l-4.295 4.295 1.41 1.41L12 13.41l4.295 4.295 1.41-1.41L13.41 12l4.295-4.295z"/></g></svg> 439 </div> 440 441 <img 442 src="<?php echo esc_url( Redux_Core::$url ); ?>assets/img/logo.svg" 443 alt="<?php esc_attr_e( 'Redux Logo', 'redux-template' ); ?>" 444 class="redux-banner-logo" 445 /> 446 447 <div class="redux-banner-full-step-header"> 448 <h2 class="redux-banner-full-step-header-title"><?php esc_html_e( 'Activate essential WordPress security and performance tools by setting up Redux', 'redux-framework' ); ?></h2> 449 </div> 450 <p class="redux-banner-full-tos-blurb"> 451 <?php self::tos_blurb( 'fullscreen' ); ?> 452 </p> 453 <p class="redux-banner-button-container"> 454 <a href="" 455 class="dops-button is-primary redux-button"> 456 <?php esc_html_e( 'Register', 'redux-framework' ); ?> 457 </a> 458 </p> 459 460 <div class="redux-banner-full-row" id="Redux-connection-cards"> 461 <div class="redux-banner-full-slide"> 462 <div class="redux-banner-full-slide-card illustration"> 463 <img 464 src="<?php echo esc_url( Redux_Core::$url ); ?>assets/img/security.svg" 465 alt="<?php esc_attr_e( 'Security & Backups', 'redux-framework' ); ?>" 466 /> 467 </div> 468 <div class="redux-banner-full-slide-card"> 469 <p> 470 <?php 471 esc_html_e( 472 'Redux protects you against brute force attacks and unauthorized logins. ' . 473 'Basic protection is always free, while premium plans add unlimited backups of your whole site, ' . 474 'spam protection, malware scanning, and automated fixes.', 475 'redux-framework' 476 ); 477 ?> 478 </p> 479 </div> 480 </div> 481 <div class="redux-banner-full-slide"> 482 <div class="redux-banner-full-slide-card illustration"> 483 <img 484 src="<?php echo esc_url( Redux_Core::$url ); ?>assets/img/redux-speed.svg" 485 alt="<?php esc_attr_e( 'Built-in Performance', 'redux-framework' ); ?>" 486 /> 487 </div> 488 <div class="redux-banner-full-slide-card"> 489 <p> 490 <?php 491 esc_html_e( 492 'Activate site accelerator tools and watch your page load times decrease—' . 493 "we'll optimize your images and serve them from our own powerful global network of servers, " . 494 'and speed up your mobile site to reduce bandwidth usage.', 495 'redux-framework' 496 ); 497 ?> 498 </p> 499 </div> 500 </div> 501 </div> 502 503 <p class="redux-banner-full-dismiss-paragraph"> 504 <a> 505 <?php 506 echo esc_html_x( 507 'Not now, thank you.', 508 'a link that closes the modal window that offers to connect Redux', 509 'redux-framework' 510 ); 511 ?> 512 </a> 513 </p> 514 </div> 515 </div> 516 <?php 517 endif; 518 } 519 520 /** 521 * Renders the legacy network connection banner. 522 */ 523 public function network_connect_notice() { 524 ?> 525 <div id="message" class="updated Redux-message"> 526 <div class="squeezer"> 527 <h2> 528 <?php 529 echo wp_kses( 530 __( 531 '<strong>Redux is activated!</strong> Each site on your network must be connected individually by an admin on that site.', 532 'Redux' 533 ), 534 array( 'strong' => array() ) 535 ); 536 ?> 537 </h2> 538 </div> 539 </div> 540 <noscript><style>#message{display:none;}</style></noscript> 541 <?php 542 } 543 544 /** 545 * Prints a TOS blurb used throughout the connection prompts. 546 * 547 * @since 4.0 548 * 549 * @echo string 550 */ 551 public static function tos_blurb( $campaign = 'options_panel' ): string { 552 return sprintf( 553 __( 'By clicking the <strong>Register</strong> button, you agree to our <a href="%1$s" target="_blank">terms of service</a>, to create an account, and to share details of your usage metrics with Redux.io.', 'redux-framework' ), 554 Redux_Functions_Ex::get_site_utm_url( 'terms', 'appsero', 'activate', $campaign ) 555 ); 556 } 557 558 } 559 } 560 // @codingStandardsIgnoreEnd