container(); if ( ! $container->has( 'license' ) ) { return false; } $license = $container->get( 'license' ); return $license instanceof License && $license->is_feature_available( $feature ); } /** * Every FeatureMap key with whether it is available, for the admin SPA. * * Built from the map so no list of feature keys is ever written by hand * (FeatureGatingSyncTest forbids one). * * @return array */ function adaf_feature_flags(): array { $flags = array(); foreach ( array_keys( FeatureMap::all() ) as $feature ) { $flags[ $feature ] = adaf_feature_available( $feature ); } return $flags; } /** * This site's tier, or free before the plugin registered its services. * * For display and for handing to the SPA. Gating always goes through * adaf_feature_available(), never through a tier comparison. */ function adaf_license_tier(): string { $container = Plugin::instance()->container(); $license = $container->has( 'license' ) ? $container->get( 'license' ) : null; return $license instanceof License ? $license->get_tier() : License::FREE; } /** * Which of two active copies of this plugin should step aside (AF-19.8). * * The WordPress.org copy carries no Freemius SDK, so Freemius cannot tell it * from Pro. With both active, the free one goes, whichever loaded first; * with two of a kind, the one that loaded second. * * @param string $loading Main file of the copy loading now. * @param string $running Main file of the copy already running. */ function adaf_copy_to_retire( string $loading, string $running ): string { $is_pro = static fn ( string $file ): bool => is_dir( dirname( $file ) . '/src/Premium' ); return $is_pro( $loading ) && ! $is_pro( $running ) ? $running : $loading; } /** * A second copy of the plugin is loading while another runs: the main file * has returned before declaring anything, and on the next admin page an * administrator's request deactivates the copy that should step aside. The * running copy serves this request; the other takes over on the next. * * @param string $loading Main file of the copy loading now. * @param string|null $running Main file of the running copy; ADAF_PLUGIN_FILE by default. */ function adaf_second_copy( string $loading, ?string $running = null ): void { $retire = adaf_copy_to_retire( $loading, $running ?? (string) ADAF_PLUGIN_FILE ); add_action( 'admin_init', static function () use ( $retire ): void { if ( ! current_user_can( 'activate_plugins' ) ) { return; } $basename = plugin_basename( $retire ); deactivate_plugins( $basename, true, is_multisite() && is_plugin_active_for_network( $basename ) ); } ); } /** * Hook the uninstall cleanup. With the Freemius SDK it runs on the SDK's * after_uninstall, once Freemius has recorded the uninstall; without it (the * WordPress.org build) on WordPress's own uninstall hook. Called on every * load, not only on activation, so a site updated from a release that used * uninstall.php is covered; WordPress writes the hook only when it changes. */ function adaf_register_uninstall(): void { if ( function_exists( 'adaf_fs' ) ) { adaf_fs()->add_action( 'after_uninstall', array( \AgentFirewall\Uninstaller::class, 'run' ) ); return; } register_uninstall_hook( ADAF_PLUGIN_FILE, array( \AgentFirewall\Uninstaller::class, 'run' ) ); }