HEX
Server: nginx
System: Linux pool195-106-36.bur.atomicsites.net 6.12.57+deb12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.57-1~bpo12+1 (2025-11-17) x86_64
User: (0)
PHP: 8.3.32
Disabled: pcntl_fork
Upload Files
File: /wordpress/plugins/wp-cloud-client/beta/src/Settings/NetworkSettingsPage.php
<?php

declare(strict_types=1);

namespace VPlugins\WPCloudClient\Settings;

use VPlugins\WPCloudClient\Analytics\GoogleAnalytics4;
use VPlugins\WPCloudClient\BusinessProfile\ProfileHelperPage;

/**
 * Network Admin-only settings page for multisite installs.
 *
 * These settings (GA4 tracking ID, mail interceptor toggle, business profile)
 * are single network-wide values shared by every site (see
 * {@see SettingsRepository::getGa4TrackingId()} for why), so on multisite
 * only a network super-admin may view or change them, and only from Network
 * Admin — {@see \VPlugins\WPCloudClient\Plugin} registers this instead of
 * SettingsPage when SettingsRepository::isMultisite() is true, so no
 * per-site admin_menu entry exists at all on multisite.
 */
final class NetworkSettingsPage {

	public const SLUG   = 'wpcc-network-settings';
	public const ACTION = 'wpcc_network_settings_save';

	private const TAB_ANALYTICS = 'analytics';
	private const TAB_MAIL      = 'email-history';
	private const TAB_PROFILE   = 'business-profile';

	/**
	 * Class constructor.
	 *
	 * @param SettingsRepository $settings      Settings repository instance.
	 * @param GoogleAnalytics4   $ga4           Google Analytics 4 instance.
	 * @param ProfileHelperPage  $profileHelper Business profile helper page.
	 */
	public function __construct(
		private readonly SettingsRepository $settings,
		private readonly GoogleAnalytics4 $ga4,
		private readonly ?ProfileHelperPage $profileHelper,
	) {}

	/**
	 * Register the settings page under Network Admin > Settings.
	 *
	 * @return void
	 */
	public function addMenuPage(): void {
		add_submenu_page(
			'settings.php',
			__( 'WP Cloud Client', 'wp-cloud-client' ),
			__( 'WP Cloud Client', 'wp-cloud-client' ),
			'manage_network_options',
			self::SLUG,
			[ $this, 'render' ],
		);
	}

	/**
	 * Get the tab definitions (slug => label).
	 *
	 * @return array<string, string>
	 */
	private function getTabs(): array {
		$tabs = [
			self::TAB_ANALYTICS => __( 'Google Analytics 4', 'wp-cloud-client' ),
			self::TAB_MAIL      => __( 'Email History', 'wp-cloud-client' ),
			self::TAB_PROFILE   => __( 'Business Profile', 'wp-cloud-client' ),
		];

		if ( $this->settings->isStaging() ) {
			unset( $tabs[ self::TAB_ANALYTICS ] );
		}

		return $tabs;
	}

	/**
	 * Get the currently active tab slug.
	 *
	 * @return string
	 */
	private function activeTab(): string {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Tab selection is not a state-changing action.
		$tab  = isset( $_GET['tab'] ) ? sanitize_key( (string) $_GET['tab'] ) : '';
		$tabs = $this->getTabs();

		return isset( $tabs[ $tab ] ) ? $tab : (string) ( array_key_first( $tabs ) ?? '' );
	}

	/**
	 * Build the URL for a given tab.
	 *
	 * Uses network_admin_url() instead of menu_page_url() because the
	 * latter always resolves against admin_url(), not the Network Admin
	 * context this page is registered in.
	 *
	 * @param string $tab Tab slug.
	 * @return string Tab URL.
	 */
	private function tabUrl( string $tab ): string {
		return add_query_arg(
			[
				'page' => self::SLUG,
				'tab'  => $tab,
			],
			network_admin_url( 'settings.php' ),
		);
	}

	/**
	 * Render the network settings page HTML.
	 *
	 * @return void
	 */
	public function render(): void {
		if ( ! current_user_can( 'manage_network_options' ) ) {
			return;
		}

		$configured = $this->settings->isConfigured();
		$tabs       = $this->getTabs();
		$activeTab  = $this->activeTab();
		?>
		<div class="wrap">
			<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>

			<?php
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag set by our own redirect after save(), not a state-changing action.
			if ( isset( $_GET['updated'] ) ) :
				?>
				<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Settings saved.', 'wp-cloud-client' ); ?></p></div>
			<?php endif; ?>

			<div class="wpcc-status-indicator" style="margin: 15px 0; padding: 10px 15px; border-left: 4px solid <?php echo $configured ? '#00a32a' : '#d63638'; ?>; background: #fff;">
				<strong><?php esc_html_e( 'Connection Status:', 'wp-cloud-client' ); ?></strong>
				<?php if ( $configured ) : ?>
					<span style="color: #00a32a;"><?php esc_html_e( 'Configured', 'wp-cloud-client' ); ?></span>
				<?php else : ?>
					<span style="color: #d63638;"><?php esc_html_e( 'Not Configured', 'wp-cloud-client' ); ?></span>
				<?php endif; ?>
				<p class="description"><?php esc_html_e( 'These settings apply to every site in this network. Authentication credentials are provisioned automatically by the server.', 'wp-cloud-client' ); ?></p>
			</div>

			<nav class="nav-tab-wrapper">
				<?php foreach ( $tabs as $slug => $label ) : ?>
					<a href="<?php echo esc_url( $this->tabUrl( $slug ) ); ?>"
						class="nav-tab <?php echo $activeTab === $slug ? 'nav-tab-active' : ''; ?>">
						<?php echo esc_html( $label ); ?>
					</a>
				<?php endforeach; ?>
			</nav>

			<div class="wpcc-tab-content" style="margin-top: 20px;">
				<?php $this->renderTabContent( $activeTab ); ?>
			</div>
		</div>
		<?php
	}

	/**
	 * Render the content for a specific tab.
	 *
	 * @param string $tab The active tab slug.
	 * @return void
	 */
	private function renderTabContent( string $tab ): void {
		if ( self::TAB_PROFILE === $tab ) {
			if ( null !== $this->profileHelper ) {
				$this->profileHelper->render();
			} else {
				printf( '<p>%s</p>', esc_html__( 'Business Profile module is not available.', 'wp-cloud-client' ) );
			}
			return;
		}

		?>
		<form action="<?php echo esc_url( network_admin_url( 'edit.php?action=' . self::ACTION ) ); ?>" method="post">
			<?php wp_nonce_field( self::ACTION ); ?>
			<input type="hidden" name="tab" value="<?php echo esc_attr( $tab ); ?>" />

			<?php if ( self::TAB_ANALYTICS === $tab ) : ?>
				<input type="hidden" name="_ga4_tab" value="1" />
				<h2><?php esc_html_e( 'Google Analytics 4', 'wp-cloud-client' ); ?></h2>
				<p><?php esc_html_e( 'Optionally connect a custom Google Analytics 4 property to track every site in this network.', 'wp-cloud-client' ); ?></p>
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row"><label for="ga4_tracking_id"><?php esc_html_e( 'Custom Google Analytics 4 Tracking ID', 'wp-cloud-client' ); ?></label></th>
						<td>
							<input type="text" id="ga4_tracking_id" name="ga4_tracking_id" value="<?php echo esc_attr( $this->settings->getGa4TrackingId() ?? '' ); ?>" class="regular-text" placeholder="G-XXXXXXXXXX" />
							<p class="description"><?php esc_html_e( 'Enter your own Google Analytics 4 Tracking ID (format: G-XXXXXXXXXX). Tracking will run concurrently with the built-in tracking.', 'wp-cloud-client' ); ?></p>
						</td>
					</tr>
				</table>
			<?php elseif ( self::TAB_MAIL === $tab ) : ?>
				<input type="hidden" name="_mail_tab" value="1" />
				<h2><?php esc_html_e( 'Email History', 'wp-cloud-client' ); ?></h2>
				<p><?php esc_html_e( 'WP Cloud Client intercepts outgoing emails to provide an Email History log in the dashboard. Disable this if you are using a third-party SMTP plugin that should handle all mail delivery.', 'wp-cloud-client' ); ?></p>
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row"><?php esc_html_e( 'Disable Website Pro Mail System', 'wp-cloud-client' ); ?></th>
						<td>
							<label>
								<input type="checkbox" id="disable_mail_interceptor" name="disable_mail_interceptor" value="1" <?php checked( $this->settings->isMailInterceptorDisabled() ); ?> />
								<?php esc_html_e( 'Disable email interception (Email History will not be available)', 'wp-cloud-client' ); ?>
							</label>
						</td>
					</tr>
				</table>
			<?php endif; ?>

			<?php submit_button( __( 'Save Network Settings', 'wp-cloud-client' ) ); ?>
		</form>
		<?php
	}

	/**
	 * Handle the network settings form submission.
	 *
	 * Hooked to network_admin_edit_{self::ACTION}. SettingsPage's Settings
	 * API/options.php form processing doesn't work in Network Admin context,
	 * so this saves directly through SettingsRepository and redirects back.
	 *
	 * Each tab submits its own form with a hidden sentinel (_ga4_tab /
	 * _mail_tab) so this only touches the setting for the tab that was
	 * actually submitted — otherwise saving the GA4 tab would read a missing
	 * disable_mail_interceptor checkbox as unchecked and silently re-enable
	 * mail interception (and vice versa).
	 *
	 * @return void
	 */
	public function save(): void {
		check_admin_referer( self::ACTION );

		if ( ! current_user_can( 'manage_network_options' ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'wp-cloud-client' ) );
		}

		if ( ! $this->settings->isStaging() && ! empty( $_POST['_ga4_tab'] ) ) {
			$this->settings->setGa4TrackingId( $this->ga4->sanitizeTrackingId( wp_unslash( (string) ( $_POST['ga4_tracking_id'] ?? '' ) ) ) );
		}

		if ( ! empty( $_POST['_mail_tab'] ) ) {
			$this->settings->setMailInterceptorDisabled( ! empty( $_POST['disable_mail_interceptor'] ) );
		}

		$tab = isset( $_POST['tab'] ) ? sanitize_key( (string) $_POST['tab'] ) : '';

		wp_safe_redirect(
			add_query_arg(
				[
					'page'    => self::SLUG,
					'tab'     => $tab,
					'updated' => 'true',
				],
				network_admin_url( 'settings.php' ),
			)
		);
		exit;
	}
}