HEX
Server: LiteSpeed
System: Linux s383.bitcommand.com 5.14.0-687.24.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Jul 9 18:14:06 EDT 2026 x86_64
User: pclast (1605)
PHP: 8.1.34
Disabled: exec,system,passthru,shell_exec,escapeshellarg,escapeshellcmd,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Upload Files
File: /home/pclast/www/wp-content/plugins/wp-rocket/inc/Engine/CDN/RocketCDN/NoticesSubscriber.php
<?php
namespace WP_Rocket\Engine\CDN\RocketCDN;

use WP_Rocket\Abstract_Render;
use WP_Rocket\Admin\Options;
use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Engine\Admin\Beacon\Beacon;
use WP_Rocket\Engine\Common\Utils;
use WP_Rocket\Engine\License\API\UserClient;
use WP_Rocket\Engine\Tracking\Tracking;
use WP_Rocket\Event_Management\Subscriber_Interface;
use WP_Rocket\Engine\Tracking\TrackingTrait;

/**
 * Subscriber for the RocketCDN notices on WP Rocket settings page
 *
 * @since 3.5
 */
class NoticesSubscriber extends Abstract_Render implements Subscriber_Interface {
	use TrackingTrait;

	/**
	 * RocketCDN API Client instance.
	 *
	 * @var APIClient
	 */
	private $api_client;

	/**
	 * Beacon instance
	 *
	 * @var Beacon
	 */
	private $beacon;

	/**
	 * UserClient instance
	 *
	 * @var UserClient
	 */
	private $user_client;

	/**
	 * Tracking instance
	 *
	 * @var Tracking
	 */
	private $tracking;

	/**
	 * WP Rocket options instance
	 *
	 * @var Options_Data
	 */
	private $options;

	/**
	 * Subscription controller instance.
	 *
	 * @var SubscriptionController
	 */
	private $subscription_controller;

	/**
	 * Constructor
	 *
	 * @param APIClient              $api_client    RocketCDN API Client instance.
	 * @param Beacon                 $beacon        Beacon instance.
	 * @param UserClient             $user_client   UserClient instance.
	 * @param Tracking               $tracking      Tracking instance.
	 * @param string                 $template_path Path to the templates.
	 * @param Options_Data           $options WP Rocket options instance.
	 * @param SubscriptionController $subscription_controller Subscription controller instance.
	 */
	public function __construct(
		APIClient $api_client,
		Beacon $beacon,
		UserClient $user_client,
		Tracking $tracking,
		$template_path,
		Options_Data $options,
		SubscriptionController $subscription_controller
	) {
		parent::__construct( $template_path );

		$this->api_client              = $api_client;
		$this->beacon                  = $beacon;
		$this->user_client             = $user_client;
		$this->tracking                = $tracking;
			$this->options             = $options;
		$this->subscription_controller = $subscription_controller;
	}

	/**
	 * Return an array of events that this subscriber wants to listen to.
	 *
	 * @return array
	 */
	public static function get_subscribed_events() {
		return [
			'admin_notices'                           => [
				[ 'purge_cache_notice' ],
				[ 'change_cname_notice' ],
				[ 'activation_failed_notice' ],
				[ 'maybe_display_rocketcdn_notice' ],
			],
			'rocket_cdn_free_before_status_indicator' => 'display_rocketcdn_cta',
			'admin_post_rocket_ignore'                => [
				[ 'track_notice_homepage_cta_click', 5 ],
				[ 'track_rocketcdn_notice_dismissed', 5 ],
			],
			'wp_ajax_rocket_ignore'                   => [ [ 'track_rocketcdn_notice_dismissed', 5 ] ],
		];
	}

	/**
	 * Displays the RocketCDN Call to Action on the CDN tab of WP Rocket settings page
	 *
	 * @since 3.5
	 *
	 * @param array $cta_data CTA data.
	 *
	 * @return void
	 */
	public function display_rocketcdn_cta( array $cta_data ) {
		/**
		 * Filters the display of the RocketCDN cta banner.
		 *
		 * @param bool $display_cta_banner; true to display, false otherwise.
		 */
		if ( ! apply_filters( 'rocket_display_rocketcdn_cta', true ) ) {
			return;
		}

		if ( $this->is_white_label_account() ) {
			return;
		}

		if ( ! rocket_is_live_site() ) {
			return;
		}

		$subscription_data = $this->api_client->get_subscription_data();

		if ( $this->subscription_controller->has_active_subscription() && $this->subscription_controller->is_paid() ) {
			return;
		}

		$pricing = $this->api_client->get_pricing_data();

		$regular_price_monthly = '';
		$regular_price_annual  = '';
		$nopromo_variant       = '--no-promo';
		$cta_small_class       = 'wpr-isHidden';
		$cta_big_class         = '';

		if ( get_user_meta( get_current_user_id(), 'rocket_rocketcdn_cta_hidden', true ) ) {
			$cta_small_class = '';
			$cta_big_class   = 'wpr-isHidden';
		}

		$small_cta_data = [
			'container_class' => $cta_small_class,
		];

		// Get button URL for one-click checkout.
		$button_url = $this->get_express_checkout_url();

		if ( is_wp_error( $pricing ) ) {
			$beacon    = $this->beacon->get_suggest( 'rocketcdn_error' );
			$more_info = sprintf(
				// translators: %1$is = opening link tag, %2$s = closing link tag.
				__( '%1$sMore Info%2$s', 'rocket' ),
				'<a href="' . esc_url( $beacon['url'] ) . '" data-beacon-article="' . esc_attr( $beacon['id'] ) . '" rel="noopener noreferrer" target="_blank">',
				'</a>'
			);

			$message = $pricing->get_error_message() . ' ' . $more_info;

			$big_cta_data = [
				'container_class' => $cta_big_class,
				'nopromo_variant' => $nopromo_variant,
				'error'           => true,
				'message'         => $message,
				'button_url'      => $button_url,
			];
		} else {
			$current_price_monthly = number_format_i18n( $pricing['monthly_price'], 2 );
			$current_price_annual  = number_format_i18n( $pricing['annual_price'] / 12, 2 );
			$promotion_campaign    = '';
			$end_date              = strtotime( $pricing['end_date'] );
			$promotion_end_date    = '';

			if (
				$pricing['is_discount_active']
				&&
				$end_date > time()
			) {
				$promotion_campaign    = $pricing['discount_campaign_name'];
				$regular_price_monthly = $current_price_monthly;
				$regular_price_annual  = $current_price_annual;
				$current_price_monthly = number_format_i18n( $pricing['discounted_price_monthly'], 2 ) . '*';
				$current_price_annual  = number_format_i18n( $pricing['discounted_price_yearly'] / 12, 2 ) . '*';
				$nopromo_variant       = '';
				$promotion_end_date    = date_i18n( get_option( 'date_format' ), $end_date );
			}

			global $wp_locale;
			$current_price_array = explode( $wp_locale->number_format['decimal_point'], $current_price_monthly );

			if ( $cta_data['limit_reached'] ) {
				$cta_big_class .= 'wpr-rocketcdn-cta---max-limit';
			}

			$big_cta_data = [
				'container_class'       => $cta_big_class,
				'promotion_campaign'    => $promotion_campaign,
				'promotion_end_date'    => $promotion_end_date,
				'nopromo_variant'       => $nopromo_variant,
				'regular_price_monthly' => $regular_price_monthly,
				'regular_price_annual'  => $regular_price_annual,
				'current_price_monthly' => $current_price_monthly,
				'current_price_annual'  => $current_price_annual,
				'button_url'            => $button_url,
				'regular_price'         => $regular_price_monthly,
				'current_price'         => $current_price_monthly,
				'current_price_array'   => [
					'major' => ! empty( $current_price_array[0] ) ? $current_price_array[0] : 0,
					'minor' => ! empty( $current_price_array[1] ) ? $current_price_array[1] : 0,
				],
			];
		}

		$big_cta_data = array_merge( $big_cta_data, $cta_data );

		echo $this->generate( 'cta-big', $big_cta_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Dynamic content is properly escaped in the view.
	}

	/**
	 * Displays a notice after purging the RocketCDN cache.
	 *
	 * @since 3.5
	 *
	 * @return void
	 */
	public function purge_cache_notice() {
		if ( ! current_user_can( 'rocket_manage_options' ) ) {
			return;
		}

		if ( 'settings_page_wprocket' !== get_current_screen()->id ) {
			return;
		}

		$purge_response = get_transient( 'rocketcdn_purge_cache_response' );

		if ( false === $purge_response ) {
			return;
		}

		$message = $purge_response['message'];

		if ( 'error' === $purge_response['status'] ) {
			$beacon    = $this->beacon->get_suggest( 'rocketcdn_error' );
			$more_info = sprintf(
				// translators: %1$is = opening link tag, %2$s = closing link tag.
				__( '%1$sMore Info%2$s', 'rocket' ),
				'<a href="' . esc_url( $beacon['url'] ) . '" data-beacon-article="' . esc_attr( $beacon['id'] ) . '" rel="noopener noreferrer" target="_blank">',
				'</a>'
			);

			$message .= ' ' . $more_info;
		}

		delete_transient( 'rocketcdn_purge_cache_response' );

		rocket_notice_html(
			[
				'status'  => $purge_response['status'],
				'message' => $message,
			]
		);
	}

	/**
	 * Checks if white label is enabled
	 *
	 * @since 3.6
	 *
	 * @return bool
	 */
	private function is_white_label_account() {
		return (bool) rocket_get_constant( 'WP_ROCKET_WHITE_LABEL_ACCOUNT' );
	}

	/**
	 * Change CName admin notice contents.
	 *
	 * @return void
	 */
	public function change_cname_notice() {
		if ( ! current_user_can( 'rocket_manage_options' ) ) {
			return;
		}

		if ( 'settings_page_wprocket' !== get_current_screen()->id ) {
			return;
		}

		$boxes = get_user_meta( get_current_user_id(), 'rocket_boxes', true );

		if ( in_array( 'rocketcdn_change_cname', (array) $boxes, true ) ) {
			return;
		}

		$old_cname = get_option( 'wp_rocket_rocketcdn_old_url' );
		if ( empty( $old_cname ) ) {
			return;
		}

		$new_subscription = $this->api_client->get_subscription_data();
		if ( empty( $new_subscription['cdn_url'] ) || $old_cname === $new_subscription['cdn_url'] ) {
			return;
		}

		$support_url = rocket_get_external_url(
			'support',
			[
				'utm_source' => 'wp_plugin',
				'utm_medium' => 'wp_rocket',
			]
		);

		$message_lines = [
			// translators: %1$s = Old CName, %2$s = New CName.
			sprintf( esc_html__( 'We\'ve updated your RocketCDN CNAME from %1$s to %2$s.', 'rocket' ), $old_cname, $new_subscription['cdn_url'] ),
			// translators: %1$s = New CName.
			sprintf( esc_html__( 'The change is already applied to the plugin settings. If you were using the CNAME in your code, make sure to update it to: %1$s.', 'rocket' ), $new_subscription['cdn_url'] ),
		];

		rocket_notice_html(
			[
				'status'         => 'info',
				'message'        => implode( '<br>', $message_lines ),
				'dismiss_button' => 'rocketcdn_change_cname',
				'id'             => 'rocketcdn_change_cname_notice',
				'action'         => sprintf( '<a href="%1$s" target="_blank" rel="noopener" class="wpr-button" id="rocketcdn-change-cname-button">%2$s</a>', $support_url, esc_html__( 'contact support', 'rocket' ) ),
			]
		);
	}

	/**
	 * Displays an admin notice when RocketCDN activation failed.
	 *
	 * Shows a notice with express checkout URL when:
	 * - is_active is false
	 * - cdn_url is empty
	 *
	 * @return void
	 */
	public function activation_failed_notice(): void {
		if ( ! current_user_can( 'rocket_manage_options' ) ) {
			return;
		}

		if ( 'settings_page_wprocket' !== get_current_screen()->id ) {
			return;
		}

		if ( $this->is_white_label_account() ) {
			return;
		}

		if ( ! $this->should_display_activation_failed_notice() ) {
			return;
		}

		if ( $this->subscription_controller->is_free() ) {
			// Send the request again (create subscription) to fix what is wrong with this account.
			$this->subscription_controller->create_subscription( true );
			return;
		}

		$express_checkout_url = $this->get_express_checkout_url();

		if ( empty( $express_checkout_url ) ) {
			return;
		}

		// Track banner view.
		$this->tracking->track_rocketcdn_activation_failed_banner_viewed();

		$message = sprintf(
			'<strong>%1$s</strong><br><br>%2$s<br>%3$s',
			esc_html__( 'RocketCDN activation incomplete', 'rocket' ),
			esc_html__( 'RocketCDN isn’t active on this website yet.', 'rocket' ),
			esc_html__( 'Click below to complete the activation. You’ll be redirected to checkout to confirm your subscription.', 'rocket' )
		);

		rocket_notice_html(
			[
				'status'      => 'error',
				'message'     => $message,
				'dismissible' => false,
				'id'          => 'rocketcdn_activation_failed_notice',
				'action'      => sprintf(
					'<a href="%1$s" target="_blank" rel="noopener" class="wpr-button" id="wpr-rocketcdn-activation-cta">%2$s</a>',
					esc_url( $express_checkout_url ),
					esc_html__( 'Complete activation', 'rocket' )
				),
			]
		);
	}

	/**
	 * Checks if the activation failed notice should be displayed.
	 *
	 * @return bool True if notice should be displayed, false otherwise.
	 */
	private function should_display_activation_failed_notice(): bool {
		if ( $this->subscription_controller->is_subscription_creation_loading() ) {
			return false;
		}

		// Do not show the notice if there is no RocketCDN user token saved:
		// this usually means the user never went through the checkout/activation flow.
		if ( empty( get_option( 'rocketcdn_user_token', '' ) ) ) {
			return false;
		}

		// Show notice when webiste is not attached.
		return ! $this->subscription_controller->is_website_attached() && $this->subscription_controller->has_active_subscription();
	}

	/**
	 * Gets the express checkout URL for RocketCDN.
	 *
	 * @return string Express checkout URL or empty string if not available.
	 */
	private function get_express_checkout_url(): string {
		$user_data = $this->user_client->get_user_data();

		if ( false === $user_data || ! isset( $user_data->rocketcdn->button->url ) || empty( $user_data->rocketcdn->button->url ) ) {
			return '';
		}

		return add_query_arg(
			[
				'dashboard_url' => rawurlencode(
					add_query_arg(
						[
							'page'               => WP_ROCKET_PLUGIN_SLUG,
							'rocketcdn_checkout' => 'true',
						],
						admin_url( 'options-general.php' )
					)
				),
			],
			esc_url_raw( $user_data->rocketcdn->button->url )
		);
	}

	/**
	 * Display RocketCDN notice on admin dashboard if flag is set and notice hasn't been dismissed
	 *
	 * @since 3.22
	 *
	 * @return void
	 */
	public function maybe_display_rocketcdn_notice() {
		/**
		 * Filters showing rocketcdn admin notices
		 *
		 * @since 3.22
		 *
		 * @param bool $show_rocketcdn_notices Show rocketcdn notices, by default it's shown.
		 */
		if ( wpm_apply_filters_typed( 'boolean', 'rocket_hide_rocketcdn_notices', false ) ) {
			return;
		}

		$previous_version = $this->options->get( 'previous_version' );
		// @phpstan-ignore-next-line
		$rocket_cdn_token = get_option( 'rocketcdn_user_token', '' );

		// Don't show the notice if RocketCDN is already active (token exists).
		if ( ! empty( $rocket_cdn_token ) ) {
			return;
		}

		// Fresh install, show new install notice.
		if ( empty( $previous_version ) ) {
			$message = sprintf(
			// translators: %1$s opening <strong> tag, %2$s closing </strong> tag.
				esc_html__(
					'%1$sNew in WP Rocket: Faster loading for your key pages%2$s',
					'rocket'
				),
				'<p><strong>',
				'</strong></p>'
			);

			$message .= sprintf(
			// translators: %1$s opening <p> tag, %2$s closing </p> tag.
				esc_html__(
					'%1$sYou can now use Content Delivery, powered by RocketCDN, to speed up your homepage and 2 more pages, at no extra cost.%2$s',
					'rocket'
				),
				'<p>',
				'</p>'
			);

			$notice_info = [
				'new_version'     => '3.22.0',
				'dismiss_button'  => 'rocketcdn_install_notice',
				'dismiss_message' => __( 'Dismiss', 'rocket' ),
				'message'         => $message,
				'action'          => 'rocketcdn_install_page',
				'status'          => 'success',
				'track_event'     => true,
			];

			Utils::display_update_notice( $notice_info, true );

			return;
		}

		$message = sprintf(
		// translators: %1$s opening <strong> tag, %2$s closing </strong> tag.
			esc_html__(
				'%1$sUse RocketCDN for free to boost up to 3 pages 🚀%2$s',
				'rocket'
			),
			'<p><strong>',
			'</strong></p>'
		);

		$message .= sprintf(
		// translators: %1$s opening <p> tag, %2$s closing </p> tag.
			esc_html__(
				'%1$sAs a WP Rocket user, you can now activate RocketCDN for free on up to 3 pages. Choose your top pages and speed up their performance worldwide!%2$s',
				'rocket'
			),
			'<p>',
			'</p>'
		);

		$notice_info = [
			'new_version'      => '3.22.0',
			'dismiss_button'   => 'rocket_update_notice',
			'dismiss_message'  => __( 'Check it later', 'rocket' ),
			'message'          => $message,
			'action'           => 'rocketcdn_upgrade_page',
			'previous_version' => $previous_version,
			'track_event'      => true,
		];

		Utils::display_update_notice( $notice_info, true );
	}

	/**
	 * Tracks the "Start with my homepage" CTA click from the RocketCDN promo admin notice.
	 *
	 * Fires before rocket_dismiss_boxes() redirects, so we can track before the exit.
	 *
	 * @return void
	 */
	public function track_notice_homepage_cta_click(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is verified later by rocket_dismiss_boxes().
		$box = isset( $_GET['box'] ) ? sanitize_key( wp_unslash( $_GET['box'] ) ) : '';

		if ( 'rocketcdn_install_notice' !== $box ) {
			return;
		}

		/**
		 * Fires when the homepage cta button is clicked.
		 *
		 * @param string $source The source of the click, either 'add_homepage_button' or 'admin_notices'.
		 */
		do_action( 'rocket_rocketcdn_add_homepage', 'admin_notices' );
	}

	/**
	 * Tracks when a RocketCDN admin notice is dismissed.
	 *
	 * Fires before rocket_dismiss_boxes() redirects, so we can track before the exit.
	 *
	 * @return void
	 */
	public function track_rocketcdn_notice_dismissed(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is verified later by rocket_dismiss_boxes().
		$box = isset( $_GET['box'] ) ? sanitize_key( wp_unslash( $_GET['box'] ) ) : '';

		$rocketcdn_boxes = [ 'rocketcdn_install_notice', 'rocket_update_notice' ];

		if ( ! in_array( $box, $rocketcdn_boxes, true ) ) {
			return;
		}

		// Track Mixpanel event immediately.
		$this->track_event( 'RocketCDN Admin Notice Dismissed' );
	}
}