%s

', esc_html( $problem ) ); } } ); return; } Schema::maybe_upgrade(); $container = self::container(); $scheduler = $container->get( Scheduler::class ); if ( $scheduler instanceof WpCronScheduler ) { $scheduler->register_hooks(); } $notifier = $container->get( Notifier::class ); $notifier->register_hooks(); add_action( 'forensix_app_deactivated', array( $notifier, 'remove_for_app' ) ); $container->get( JobRunner::class )->register_hooks(); $container->get( ActivityLog::class )->register_hooks(); $container->get( FindingStore::class )->register_hooks(); $container->get( Settings::class )->register( 'core', self::core_defaults(), array( self::class, 'sanitize_core_settings' ) ); ( new Api( $container ) )->register_hooks(); if ( is_admin() ) { ProVersionNotice::register(); ( new Menu() )->register_hooks(); } /** * Fires when the core services are ready, before apps start. * Add-ons (Forensix Digital Pro) add their services and filters here. * * @param Container $container Service container. */ do_action( 'forensix_loaded', $container ); // Apps start on init: their names and descriptions are translated, and WordPress 6.7+ // refuses translations before the theme is set up. add_action( 'init', array( self::class, 'start_apps' ), 1 ); } /** * Registers app definitions and starts the active apps. Hooked to init, priority 1. */ public static function start_apps(): void { $container = self::container(); add_action( 'forensix_register_apps', array( self::class, 'register_apps' ) ); $registry = $container->get( AppRegistry::class ); $registry->boot(); if ( false !== get_option( self::RESTORE_OPTION ) ) { self::restore_schedules( $registry, $container->get( JobRunner::class ) ); } /** * Fires after all active apps have started. * * @param Container $container Service container. */ do_action( 'forensix_ready', $container ); } /** * Registers the apps of the free plugin. Hooked to forensix_register_apps. * * @param AppRegistry $registry Registry. */ public static function register_apps( AppRegistry $registry ): void { $registry->register( SiteScanApp::definition() ); $registry->register( SeoApp::definition() ); $registry->register( MediaApp::definition() ); $registry->register( FormsApp::definition() ); $registry->register( MailApp::definition() ); $registry->register( BusinessApp::definition() ); $registry->register( LaunchApp::definition() ); $registry->register( MaintenanceApp::definition() ); $registry->register( ContentApp::definition() ); $registry->register( CommerceApp::definition() ); $registry->register( PreCheckApp::definition() ); $registry->register( A11yApp::definition() ); $registry->register( PerfApp::definition() ); $registry->register( MonitorApp::definition() ); $registry->register( GoogleApp::definition() ); $registry->register( ObservationsApp::definition() ); } /** * Activation: refuses unsupported environments, creates tables and grants capabilities. */ public static function activate(): void { $problems = Requirements::problems( PHP_VERSION, get_bloginfo( 'version' ), is_multisite() ); if ( array() !== $problems ) { deactivate_plugins( plugin_basename( FORENSIX_FILE ) ); wp_die( esc_html( implode( ' ', $problems ) ), '', array( 'back_link' => true ) ); } // Before the tables are created, so data from versions up to 1.0.6 is moved, not shadowed. PrefixMigration::maybe_run(); Schema::install(); Capabilities::grant_defaults(); // Apps re-create their schedules on the next request, once all add-ons are loaded. update_option( self::RESTORE_OPTION, 1, false ); } /** * Deactivation: stops every job and schedule. All data is kept (section 8.5). */ public static function deactivate(): void { $container = self::container(); $container->get( JobRunner::class )->stop_all( $container->get( AppRegistry::class )->active_ids() ); /** * Fires on plugin deactivation. */ do_action( 'forensix_deactivated' ); } /** * Lets every running app re-create its schedules after the plugin was re-activated. * * @param AppRegistry $registry Registry. * @param JobRunner $runner Job runner. */ private static function restore_schedules( AppRegistry $registry, JobRunner $runner ): void { delete_option( self::RESTORE_OPTION ); $runner->schedule_watchdog(); $registry->reactivate_running(); } /** * Defaults of the core settings scope. * * @return array */ public static function core_defaults(): array { return array( 'remove_all_data' => false, 'activity_retention_days' => ActivityLog::DEFAULT_RETENTION_DAYS, ); } /** * Cleans the core settings before they are saved. Unknown keys are kept untouched. * * @param array $values Raw values. * @return array */ public static function sanitize_core_settings( array $values ): array { if ( array_key_exists( 'remove_all_data', $values ) ) { $values['remove_all_data'] = (bool) $values['remove_all_data']; } if ( array_key_exists( 'activity_retention_days', $values ) ) { $values['activity_retention_days'] = max( 30, min( 3650, (int) $values['activity_retention_days'] ) ); } return $values; } /** * Registers the core services. */ private static function build(): Container { $container = new Container(); $container->set( Settings::class, static fn(): Settings => new Settings() ); $container->set( Entitlements::class, static fn(): Entitlements => new Entitlements() ); $container->set( Notifier::class, static fn(): Notifier => new Notifier() ); $container->set( Scheduler::class, static fn(): Scheduler => ActionSchedulerScheduler::available() ? new ActionSchedulerScheduler() : new WpCronScheduler() ); $container->set( JobRunner::class, static fn( Container $c ): JobRunner => new JobRunner( new WpdbJobStore(), $c->get( Scheduler::class ) ) ); $container->set( FindingStore::class, static fn(): FindingStore => new FindingStore() ); $container->set( SourceStore::class, static fn(): SourceStore => new SourceStore() ); $container->set( ActivityLog::class, static function ( Container $c ): ActivityLog { $log = new ActivityLog( new WpdbActivityStore() ); // Repairs change source values and SEO fields. Undo stays available without the add-on. $log->register_undo_handler( SourceUndo::ACTION, new SourceUndo( $c->get( SourceStore::class ), $c->get( FindingStore::class ) ) ); $log->register_undo_handler( SeoFieldUndo::ACTION, new SeoFieldUndo( new SeoProvider() ) ); $log->register_undo_handler( AltText::ACTION, new AltText() ); $log->register_undo_handler( SingleCompressor::ACTION, new SingleCompressor() ); return $log; } ); $container->set( Http::class, static fn(): Http => new Http() ); $container->set( AppRegistry::class, static fn( Container $c ): AppRegistry => new AppRegistry( $c, $c->get( Entitlements::class ), $c->get( Settings::class ) ) ); return $container; } }