File: /wordpress/plugins/wp-cloud-client/1.1.1/src/Plugin.php
<?php
declare(strict_types=1);
namespace VPlugins\WPCloudClient;
use VPlugins\WPCloudClient\Analytics\GoogleAnalytics4;
use VPlugins\WPCloudClient\Cleanup\GtmCleanup;
use VPlugins\WPCloudClient\BusinessProfile\FieldRenderer;
use VPlugins\WPCloudClient\BusinessProfile\ProfileRepository;
use VPlugins\WPCloudClient\BusinessProfile\LegacyShortcodeCompat;
use VPlugins\WPCloudClient\BusinessProfile\ProfileAdminNotice;
use VPlugins\WPCloudClient\BusinessProfile\ProfileBlock;
use VPlugins\WPCloudClient\BusinessProfile\ProfileHelperPage;
use VPlugins\WPCloudClient\BusinessProfile\ProfileShortcode;
use VPlugins\WPCloudClient\Api\Controller\ActionController;
use VPlugins\WPCloudClient\Api\Controller\MailController;
use VPlugins\WPCloudClient\Api\Controller\WooCommerceController;
use VPlugins\WPCloudClient\Api\RestRouter;
use VPlugins\WPCloudClient\Auth\AuthMiddleware;
use VPlugins\WPCloudClient\Auth\ApiKeyAuthenticator;
use VPlugins\WPCloudClient\Access\LoginInterceptor;
use VPlugins\WPCloudClient\Access\LoginTokenManager;
use VPlugins\WPCloudClient\Handler\CheckUserHandler;
use VPlugins\WPCloudClient\Handler\CheckPluginUpdatesHandler;
use VPlugins\WPCloudClient\Handler\CheckThemeUpdatesHandler;
use VPlugins\WPCloudClient\Handler\GetSiteOptionsHandler;
use VPlugins\WPCloudClient\Handler\HandlerRegistry;
use VPlugins\WPCloudClient\Handler\HealthCheckHandler;
use VPlugins\WPCloudClient\Handler\ListPluginsHandler;
use VPlugins\WPCloudClient\Handler\ListThemesHandler;
use VPlugins\WPCloudClient\Handler\LogoutHandler;
use VPlugins\WPCloudClient\Handler\OneClickLoginHandler;
use VPlugins\WPCloudClient\Handler\PhpVerifyHandler;
use VPlugins\WPCloudClient\Handler\SetSiteOptionsHandler;
use VPlugins\WPCloudClient\Handler\SiteDetailsHandler;
use VPlugins\WPCloudClient\Handler\SiteSummaryHandler;
use VPlugins\WPCloudClient\Handler\SyncProfileHandler;
use VPlugins\WPCloudClient\Handler\ActivatePluginHandler;
use VPlugins\WPCloudClient\Handler\BootstrapSiteHandler;
use VPlugins\WPCloudClient\Handler\TriggerAllPluginUpdatesHandler;
use VPlugins\WPCloudClient\Handler\TriggerAllThemeUpdatesHandler;
use VPlugins\WPCloudClient\Handler\TriggerPluginUpdateHandler;
use VPlugins\WPCloudClient\Handler\TriggerThemeUpdateHandler;
use VPlugins\WPCloudClient\Handler\TriggerJetpackBackupHandler;
use VPlugins\WPCloudClient\Handler\WooCommerceDataHandler;
use VPlugins\WPCloudClient\Debug\WpConfigEditor;
use VPlugins\WPCloudClient\Handler\GetDebugStatusHandler;
use VPlugins\WPCloudClient\Handler\NetworkInstallHandler;
use VPlugins\WPCloudClient\Handler\SetDebugModeHandler;
use VPlugins\WPCloudClient\Mail\MailCleaner;
use VPlugins\WPCloudClient\Cli\BackfillCommand;
use VPlugins\WPCloudClient\Integration\WooCommerceHelper;
use VPlugins\WPCloudClient\Integration\WooCommerceOrderCaller;
use VPlugins\WPCloudClient\Integration\WooCommerceProductCaller;
use VPlugins\WPCloudClient\Multisite\DomainProtector;
use VPlugins\WPCloudClient\Multisite\SubsiteLimit;
use VPlugins\WPCloudClient\Multisite\SubsiteSync;
use VPlugins\WPCloudClient\Multisite\SuperAdminManager;
use VPlugins\WPCloudClient\Mail\MailManager;
use VPlugins\WPCloudClient\Mail\MailSettings;
use VPlugins\WPCloudClient\Mail\MailTable;
use VPlugins\WPCloudClient\Settings\SettingsPage;
use VPlugins\WPCloudClient\SiteOptions\OptionRegistry;
use VPlugins\WPCloudClient\Settings\SettingsRepository;
use VPlugins\WPCloudClient\Support\Clock;
use VPlugins\WPCloudClient\Support\Logger;
use VPlugins\WPCloudClient\Support\RollbackManager;
final class Plugin {
private static ?self $instance = null;
private bool $booted = false;
private readonly SettingsRepository $settings;
private readonly HandlerRegistry $handlers;
private readonly Logger $logger;
private readonly Clock $clock;
private readonly MailTable $mailTable;
private LoginTokenManager $tokenManager;
private WooCommerceDataHandler $wooCommerceDataHandler;
/**
* Initialize plugin dependencies.
*/
private function __construct() {
$this->settings = new SettingsRepository();
$this->handlers = new HandlerRegistry();
$this->logger = new Logger();
$this->clock = new Clock();
$this->mailTable = MailTable::getInstance();
}
/**
* Retrieve the singleton plugin instance.
*
* @return self The plugin instance.
*/
public static function instance(): self {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Boot the plugin by registering handlers and hooks.
*
* @return void
*/
public function boot(): void {
if ( $this->booted ) {
return;
}
$this->booted = true;
$this->registerHandlers();
$this->registerHooks();
}
/**
* Register all action handlers with the handler registry.
*
* @return void
*/
private function registerHandlers(): void {
$this->tokenManager = new LoginTokenManager();
// Integration helpers
$optionRegistry = new OptionRegistry();
// Core handlers
$this->handlers->register( new HealthCheckHandler( $this->settings, $this->clock ) );
$this->handlers->register( new OneClickLoginHandler( $this->tokenManager ) );
// Site info handlers
$this->handlers->register( new SiteSummaryHandler( $this->settings ) );
$this->handlers->register( new SiteDetailsHandler() );
$this->handlers->register( new CheckUserHandler() );
$this->handlers->register( new PhpVerifyHandler() );
$this->handlers->register( new LogoutHandler() );
// Integration handlers
$this->wooCommerceDataHandler = new WooCommerceDataHandler( $this->settings );
$this->handlers->register( $this->wooCommerceDataHandler );
$this->handlers->register( new SyncProfileHandler() );
// Jetpack handlers
$this->handlers->register( new TriggerJetpackBackupHandler( $this->settings, $this->logger ) );
// Site options handlers
$this->handlers->register( new GetSiteOptionsHandler( $optionRegistry ) );
$this->handlers->register( new SetSiteOptionsHandler( $optionRegistry ) );
$this->handlers->register( new BootstrapSiteHandler( $optionRegistry ) );
// Plugin update handlers
$this->handlers->register( new CheckPluginUpdatesHandler() );
$this->handlers->register( new ListPluginsHandler() );
$rollbackManager = new RollbackManager();
$rollbackManager->migrateFromLegacyKey();
$triggerPluginUpdate = new TriggerPluginUpdateHandler( $rollbackManager, $this->logger );
$this->handlers->register( $triggerPluginUpdate );
$this->handlers->register( new TriggerAllPluginUpdatesHandler( $triggerPluginUpdate, $this->logger ) );
$this->handlers->register( new ActivatePluginHandler() );
// Theme update handlers
$this->handlers->register( new CheckThemeUpdatesHandler() );
$this->handlers->register( new ListThemesHandler() );
$triggerThemeUpdate = new TriggerThemeUpdateHandler( $rollbackManager, $this->logger );
$this->handlers->register( $triggerThemeUpdate );
$this->handlers->register( new TriggerAllThemeUpdatesHandler( $triggerThemeUpdate, $this->logger ) );
// Multisite network installer
if ( \defined( 'WP_ALLOW_MULTISITE' ) && \WP_ALLOW_MULTISITE ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
$this->handlers->register( new NetworkInstallHandler( $this->logger ) );
}
// Debug
$debugEditor = new WpConfigEditor();
$this->handlers->register( new GetDebugStatusHandler( $debugEditor ) );
$this->handlers->register( new SetDebugModeHandler( $debugEditor ) );
}
/**
* Register WordPress hooks for REST API, login, analytics, and admin.
*
* @return void
*/
private function registerHooks(): void {
// REST API
$authenticator = new ApiKeyAuthenticator( $this->settings );
$authMiddleware = new AuthMiddleware( $authenticator );
$controller = new ActionController( $this->handlers, $this->logger );
$wooCommerce = new WooCommerceController( $this->wooCommerceDataHandler, $this->logger );
$mailApi = new MailController( $this->mailTable, $this->logger );
$router = new RestRouter( $authMiddleware, $controller, $wooCommerce, $mailApi );
add_action( 'rest_api_init', [ $router, 'register' ] );
// One-click login interceptor
$loginInterceptor = new LoginInterceptor( $this->tokenManager, $this->logger );
add_action( 'init', [ $loginInterceptor, 'handle' ] );
// Google Analytics 4
$ga4 = new GoogleAnalytics4( $this->settings );
if ( ! $this->settings->isStaging() ) {
$ga4->register();
}
// Ensure the mail table exists on every boot (dbDelta is idempotent).
try {
$this->mailTable->initialize();
} catch ( \Throwable $e ) {
$this->logger->error( 'Mail table initialization failed', $e );
}
// Mail interceptor + REST cleanup endpoint
$mailSettings = new MailSettings( $this->settings );
$mailManager = MailManager::init( $this->mailTable, $this->settings, $this->logger );
$mailCleaner = new MailCleaner( $mailManager, $authMiddleware, $this->logger );
if ( ! $mailSettings->isDisabled() ) {
add_action( 'init', [ $mailManager, 'register' ] );
}
add_action( 'rest_api_init', [ $mailCleaner, 'register' ] );
// WP-Cron retry: drain any emails that failed to notify on the first attempt.
// phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval -- 5-min retry is intentional for email notify resilience.
add_filter( 'cron_schedules', [ $this, 'addCronSchedules' ] );
add_action( 'wpcc_mail_notify', [ $mailManager, 'flush' ] );
if ( ! wp_next_scheduled( 'wpcc_mail_notify' ) ) {
wp_schedule_event( time(), 'wpcc_5min', 'wpcc_mail_notify' );
}
// Business Profile rendering (isolated so a failure here does not break admin settings)
$profileHelper = null;
try {
$profileRepo = new ProfileRepository();
$fieldRenderer = new FieldRenderer();
$profileShortcode = new ProfileShortcode( $profileRepo, $fieldRenderer );
$profileShortcode->register();
$legacyCompat = new LegacyShortcodeCompat( $profileRepo, $fieldRenderer );
$legacyCompat->register();
$profileBlock = new ProfileBlock( $profileRepo );
$profileBlock->register();
$profileNotice = new ProfileAdminNotice( $profileRepo );
$profileNotice->register();
$profileHelper = new ProfileHelperPage( $profileRepo, $fieldRenderer, $profileNotice );
} catch ( \Throwable $e ) {
$this->logger->error( 'Failed to register BusinessProfile module', $e );
}
// Admin settings
$settingsPage = new SettingsPage( $this->settings, $ga4, $mailSettings, $profileHelper );
add_action( 'admin_menu', [ $settingsPage, 'addMenuPage' ] );
add_action( 'admin_init', [ $settingsPage, 'registerSettings' ] );
if ( null !== $profileHelper ) {
add_action( 'admin_enqueue_scripts', [ $profileHelper, 'enqueueAssets' ] );
}
// Multisite features
if ( $this->settings->isMultisite() ) {
( new DomainProtector() )->register();
( new SubsiteLimit( $this->settings ) )->register();
( new SubsiteSync( $this->settings, $this->logger ) )->register();
( new SuperAdminManager() )->register();
}
// WooCommerce outbound callers — POST order and product events to wp-cloud-manager.
if ( WooCommerceHelper::isActive() ) {
( new WooCommerceOrderCaller( $this->settings, $this->logger ) )->register();
( new WooCommerceProductCaller( $this->settings, $this->logger ) )->register();
}
// WP-CLI commands (backfill after migration cutover).
if ( defined( 'WP_CLI' ) && WP_CLI ) {
$backfillCommand = new BackfillCommand( $this->settings, $this->logger );
\WP_CLI::add_command( 'wpcc', $backfillCommand );
}
add_action( 'plugins_loaded', [ $this, 'maybeMigrate' ] );
}
/**
* Run first-install and upgrade migrations on every boot, gated by a stored version key.
*
* Runs on `plugins_loaded`. As a mu-plugin, activation/deactivation hooks never fire,
* so all one-time setup lives here and executes once per version change.
*
* @return void
*/
public function maybeMigrate(): void {
if ( get_option( 'wpcc_db_version' ) === VPLUGINS_WPCC_VERSION ) {
return;
}
if ( empty( $this->settings->all() ) ) {
$this->settings->update(
[
'debug_mode' => '',
'ga4_tracking_id' => '',
'disable_mail_interceptor' => '',
]
);
}
$this->settings->purge( [ 'shared_secret', 'shared_secret_previous', 'environment', 'api_key', 'wp_cloud_manager_url' ] );
// One-time GTM cleanup (WSP-3395): strip GTM-5VJ33FV6 from WPCode/IHAF options.
( new GtmCleanup( $this->logger ) )->run();
update_option( 'wpcc_db_version', VPLUGINS_WPCC_VERSION );
}
/**
* Register custom WP-Cron intervals used by this plugin.
*
* @internal WordPress `cron_schedules` filter callback.
*
* @param array<string, array{interval: int, display: string}> $schedules Existing schedules.
* @return array<string, array{interval: int, display: string}>
*/
public function addCronSchedules( array $schedules ): array {
$schedules['wpcc_5min'] = [
// phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval -- 5-min retry is intentional for email notify resilience.
'interval' => 300,
'display' => 'Every 5 Minutes (WP Cloud Client)',
];
return $schedules;
}
}