activation routine for all registered modules. */ public function run_activation() { self::load_module_file( 'setup.php' ); do_action( 'itsec_modules_do_plugin_activation' ); } /** * Run the deactivation routine for all registered modules. */ public function run_deactivation() { self::load_module_file( 'setup.php' ); do_action( 'itsec_modules_do_plugin_deactivation' ); } /** * Run the uninstall routine for all registered modules. */ public static function run_uninstall() { self::load_module_file( 'setup.php' ); do_action( 'itsec_modules_do_plugin_uninstall' ); } /** * Run the upgrade routine for all registered modules. * * @param int $old_version * @param int $new_version */ public function run_upgrade( $old_version, $new_version ) { self::load_module_file( 'setup.php' ); do_action( 'itsec_modules_do_plugin_upgrade', $old_version, $new_version ); } /** * Get the container. * * @return ContainerInterface */ public static function get_container() { if ( ! self::get_instance()->initialized_container ) { self::initialize_container(); } return self::get_instance()->container; } /** * Load the settings controller for all registered modules. * * This function can only be run once per-request. */ public function load_settings_page() { self::load_module_file( 'settings-page.php' ); } /** * Get labels for a module. * * @param string $module * * @return array */ public static function get_labels( $module ) { $config = self::get_config( $module ); if ( $config ) { return [ 'title' => $config->translate( Module_Config::T_ABOUT )->get_title(), 'description' => $config->translate( Module_Config::T_ABOUT )->get_description(), ]; } if ( ! isset( self::get_instance()->labels[ $module ] ) ) { self::get_instance()->labels[ $module ] = []; self::load_module_file( 'labels.php', $module, function ( $labels, $module ) { if ( is_array( $labels ) ) { self::get_instance()->labels[ $module ] = $labels; } } ); } return self::get_instance()->labels[ $module ]; } /** * Validates a module's requirements. * * @param string $module * @param string $mode The mode to evaluate for. Either 'activate' or 'run'. * * @return WP_Error */ public static function validate_module_requirements( string $module, string $mode ): WP_Error { if ( defined( 'ITSEC_IGNORE_MODULE_REQUIREMENTS' ) && ITSEC_IGNORE_MODULE_REQUIREMENTS ) { return new WP_Error(); } $config = self::get_config( $module ); if ( ! $config || ! $config->get_requirements() ) { return new WP_Error(); } $requirements = $config->get_requirements(); $check = []; if ( isset( $requirements['ssl'] ) && ( $mode === 'activate' || $requirements['ssl']['validate'] === $mode ) ) { $check['ssl'] = true; } if ( isset( $requirements['feature-flags'] ) && ( $mode === 'activate' || $requirements['feature-flags']['validate'] === $mode ) ) { $check['feature-flags'] = $requirements['feature-flags']['flags']; } if ( isset( $requirements['multisite'] ) && ( $mode === 'activate' || $requirements['multisite']['validate'] === $mode ) ) { $check['multisite'] = $requirements['multisite']['status']; } if ( isset( $requirements['server'] ) && ( $mode === 'activate' || $requirements['server']['validate'] === $mode ) ) { $check['server'] = [ 'php' => $requirements['server']['php'] ?? null, 'extensions' => $requirements['server']['extensions'] ?? [], ]; } if ( isset( $requirements['load'] ) && ( $mode === 'activate' || $requirements['load']['validate'] === $mode ) ) { $check['load'] = $requirements['load']['type']; } if ( isset( $requirements['ip'] ) && ( $mode === 'activate' || $requirements['ip']['validate'] === $mode ) ) { $check['ip'] = true; } return ITSEC_Lib::evaluate_requirements( $check, $mode === 'activate' ); } public function get_export_slug(): string { return 'modules'; } public function get_export_title(): string { return __( 'Features', 'better-wp-security' ); } public function get_export_description(): string { return __( 'List of active modules.', 'better-wp-security' ); } public function get_export_options_schema(): array { return []; } public function get_export_schema(): array { return [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'string', 'enum' => [ 'active', 'inactive' ], ], ]; } public function get_transformations(): array { return []; } public function export( $options ): Result { $data = []; foreach ( self::get_available_modules() as $module ) { if ( ! self::is_always_active( $module ) ) { $data[ $module ] = self::is_active( $module ) ? 'active' : 'inactive'; } } return Result::success( $data ); } public function import( Export $from, Import_Context $context ): Result { $result = Result::success(); if ( ! $from->has_data( $this->get_export_slug() ) ) { return $result; } foreach ( $from->get_data( $this->get_export_slug() ) as $module => $status ) { $success = true; if ( $status === 'active' && ! self::is_active( $module ) ) { $success = self::activate( $module ); } elseif ( $status === 'inactive' && self::is_active( $module ) ) { $success = self::deactivate( $module ); } if ( is_wp_error( $success ) ) { $result->add_warning_message( ...ITSEC_Lib::get_error_strings( $success ) ); } } return $result; } /** * Validates a module's schema. * * @param array $config * * @return true|WP_Error */ private static function validate_module_config( $config ) { $self = self::get_instance(); if ( ! $self->module_schema ) { $self->module_schema = ITSEC_Lib::resolve_schema_refs( json_decode( file_get_contents( __DIR__ . '/module-schema.json' ), true ) ); } return rest_validate_value_from_schema( $config, $self->module_schema, 'config' ); } /** * Initializes the module's feature flags. * * @param Module_Config $config */ private static function initialize_feature_flags( Module_Config $config ) { if ( ! $config->get_feature_flags() ) { return; } $flags = $config->get_feature_flags(); if ( self::is_active( $config->get_id() ) ) { $register = array_keys( $flags ); } else { $register = $config->get_requirements()['feature-flags']['flags'] ?? []; } foreach ( $register as $flag ) { if ( isset( $flags[ $flag ] ) ) { ITSEC_Lib_Feature_Flags::register_flag( $flag, $flags[ $flag ] ); } } } /** * Checks if the given module has the requested file. * * @param string $module The module id. * @param string $file The filename to check. * * @return bool */ private static function module_has_file( $module, $file ) { return file_exists( self::get_instance()->_module_paths[ $module ] . '/' . $file ); } /** * Transforms a module specifier to a list of modules. * * @param string|array $modules The modules specifier. * * @return string[] */ private static function transform_modules_specifier( $modules ) { if ( ':all' === $modules ) { return self::get_available_modules(); } if ( ':active' === $modules ) { return self::get_active_modules_to_run(); } if ( ':active-early' === $modules ) { return array_filter( self::get_active_modules_to_run(), function ( $module ) { return self::get_instance()->module_config[ $module ]->can_load_early(); } ); } if ( ':active-normal' === $modules ) { return array_filter( self::get_active_modules_to_run(), function ( $module ) { return ! self::get_instance()->module_config[ $module ]->can_load_early(); } ); } if ( is_string( $modules ) ) { return [ $modules ]; } if ( is_array( $modules ) ) { return $modules; } return []; } private function run( $definition ) { if ( $definition && is_string( $definition ) ) { $object = $this->container->get( $definition ); if ( $object instanceof Runnable ) { $object->run(); } } } /** * Load the container definitions for a module. * * @param string $module */ private function load_container_definitions( $module ) { if ( ! isset( $this->loaded_containers[ $module ] ) && isset( $this->_module_paths[ $module ] ) ) { $path = $this->_module_paths[ $module ] . '/container.php'; if ( file_exists( $this->_module_paths[ $module ] . '/container.php' ) && $register = include( $path ) ) { $this->loaded_containers[ $module ] = true; $register( $this->pimple ); } else { $this->loaded_containers[ $module ] = false; } } } } ITSEC_Modules::get_instance();