*/ const FEATURES = array( 'redirects' => array( 'Redirects', true ), 'blocks' => array( 'Blocks', true ), 'indexing' => array( 'Indexing', false ), 'image_seo' => array( 'Image_Seo', false ), 'technical_tools' => array( 'Technical_Tools', false ), 'integrations' => array( 'Integrations', true ), ); /** * Fully qualified class name for one of a feature's classes. * * @param string $id Feature id. * @param string $class Class name inside the feature namespace. * @return string */ public static function class_name( $id, $class ) { return __NAMESPACE__ . '\\' . self::FEATURES[ $id ][0] . '\\' . $class; } /** * Adds every feature's defaults and module switch to the core defaults. * * @param array $defaults Core defaults. * @return array */ public static function add_defaults( array $defaults ) { foreach ( self::FEATURES as $id => $feature ) { $settings = self::class_name( $id, 'Settings' ); $defaults = array_merge( $defaults, $settings::defaults() ); $defaults['modules'][ $id ] = $feature[1]; } return $defaults; } /** * Module cards for the Modules screen. * * @return array[] */ public static function modules() { $modules = array(); foreach ( array_keys( self::FEATURES ) as $id ) { $settings = self::class_name( $id, 'Settings' ); $modules[ $id ] = $settings::module(); } return $modules; } /** * Settings tabs, shown even while a module is off so people can see what * it offers before switching it on. * * @return array[] */ public static function tabs() { $tabs = array(); foreach ( array_keys( self::FEATURES ) as $id ) { $settings = self::class_name( $id, 'Settings' ); $tab = $settings::tab(); if ( $tab ) { $tabs[ $id ] = $tab; } } return $tabs; } /** * Starts every feature that is switched on. * * @return object[] Feature instances keyed by id. */ public static function boot() { $loaded = array(); foreach ( array_keys( self::FEATURES ) as $id ) { if ( ! Modules::is_active( $id ) ) { continue; } $feature = self::class_name( $id, 'Feature' ); if ( method_exists( $feature, 'install' ) ) { add_action( 'init', array( $feature, 'maybe_install' ), 1 ); } $loaded[ $id ] = new $feature(); } return $loaded; } /** * Creates tables for a feature the moment it is switched on, so its * screens work on the very next request. * * @param string $id Module id. * @param bool $active New state. * @return void */ public static function on_module_toggled( $id, $active ) { if ( ! $active || ! isset( self::FEATURES[ $id ] ) ) { return; } $feature = self::class_name( $id, 'Feature' ); if ( method_exists( $feature, 'install' ) ) { $feature::install(); } } /** * Every table the features own, without the site prefix. * * @return string[] */ public static function tables() { $tables = array(); foreach ( array_keys( self::FEATURES ) as $id ) { $feature = self::class_name( $id, 'Feature' ); if ( method_exists( $feature, 'tables' ) ) { $tables = array_merge( $tables, $feature::tables() ); } } return $tables; } /** * Lets WordPress drop feature tables when a network site is deleted. * * @param string[] $tables Tables to drop. * @return string[] */ public static function drop_tables_for_site( $tables ) { global $wpdb; foreach ( self::tables() as $table ) { $tables[] = $wpdb->prefix . $table; } return $tables; } /** * Removes every feature's tables and options. Called from uninstall.php. * * @return void */ public static function uninstall() { global $wpdb; foreach ( self::tables() as $table ) { $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $wpdb->prefix . $table ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange } foreach ( array_keys( self::FEATURES ) as $id ) { $feature = self::class_name( $id, 'Feature' ); delete_option( 'tagwright_seo_' . $id . '_db_version' ); if ( method_exists( $feature, 'uninstall' ) ) { $feature::uninstall(); } } } }