$taxonomy, 'hide_empty' => false, ) ); return is_wp_error( $count ) ? null : (int) $count; } /** * How many attachments the site has, in any status. * * @return int|null The count, or null when WordPress cannot count them. */ private static function attachment_count(): ?int { if ( ! function_exists( 'wp_count_posts' ) ) { return null; } $counts = wp_count_posts( 'attachment' ); if ( ! is_object( $counts ) ) { return null; } $total = 0; foreach ( get_object_vars( $counts ) as $value ) { if ( is_numeric( $value ) ) { $total += (int) $value; } } return $total; } /** * Add one check to the report. * * @param array $checks The checks so far, changed in place. * @param string $id The check's key. * @param string $label Its name. * @param string $status pass, warn or info (anything else counts as info). * @param string $detail What was found. */ private static function add( array &$checks, string $id, string $label, string $status, string $detail ): void { if ( ! in_array( $status, array( 'pass', 'warn', 'info' ), true ) ) { $status = 'info'; } $checks[] = array( 'id' => $id, 'label' => $label, 'status' => $status, 'detail' => $detail, ); } /** * Every release health check, with the number of warnings. * * @return array Checks and the warning count. */ public static function report(): array { $pre = Settings::preflight(); $backup = Backup::capabilities(); $background = Scheduler::status(); $connection = Remote_Backup::connection(); $media = self::attachment_count(); $folders = self::count_terms( Library::FOLDER ); $collections = self::count_terms( Library::COLLECTION ); $checks = array(); self::add( $checks, 'single_site', __( 'Single-site WordPress', 'affstackdev-image-manager' ), ! empty( $pre['single_site_supported'] ) ? 'pass' : 'warn', ! empty( $pre['single_site_supported'] ) ? __( 'Supported.', 'affstackdev-image-manager' ) : __( 'Image Manager runs on single-site WordPress; Multisite networks are not supported.', 'affstackdev-image-manager' ) ); self::add( $checks, 'html_api', __( 'WordPress HTML API', 'affstackdev-image-manager' ), ! empty( $pre['html_api'] ) ? 'pass' : 'warn', ! empty( $pre['html_api'] ) ? __( 'Required HTML parser is available.', 'affstackdev-image-manager' ) : __( 'WordPress 6.5+ HTML Tag Processor is unavailable.', 'affstackdev-image-manager' ) ); self::add( $checks, 'public_storage', __( 'Generated-image storage', 'affstackdev-image-manager' ), ! empty( $pre['storage_ready'] ) ? 'pass' : 'warn', ! empty( $pre['storage_ready'] ) ? __( 'Managed derivative storage is writable with the configured reserve.', 'affstackdev-image-manager' ) : __( 'Managed derivative storage is unavailable, not writable, or below the configured reserve.', 'affstackdev-image-manager' ) ); $codec_available = false; foreach ( (array) ( $pre['compression_capabilities'] ?? array() ) as $cap ) { if ( is_array( $cap ) && ! empty( $cap['available'] ) ) { $codec_available = true; break; } } self::add( $checks, 'codec', __( 'Image processing capability', 'affstackdev-image-manager' ), $codec_available ? 'pass' : 'warn', $codec_available ? __( 'At least one tested local processing path is available.', 'affstackdev-image-manager' ) : __( 'No tested local processing path is currently available; originals remain usable.', 'affstackdev-image-manager' ) ); $bg = (string) ( $background['code'] ?? '' ); // F-IM-57: Scheduler::status() gives its state as code. self::add( $checks, 'scheduler', __( 'Independent background execution', 'affstackdev-image-manager' ), 'confirmed' === $bg ? 'pass' : 'warn', 'confirmed' === $bg ? __( 'A recent independent background probe executed.', 'affstackdev-image-manager' ) : __( 'Independent background execution has not been proven recently. Browser/PHP assistance can still advance authorized work.', 'affstackdev-image-manager' ) ); self::add( $checks, 'private_backup_storage', __( 'Private backup staging', 'affstackdev-image-manager' ), ! empty( $backup['storage']['available'] ) ? 'pass' : 'warn', ! empty( $backup['storage']['available'] ) ? (string) ( $backup['storage']['note'] ?? __( 'Private backup storage is available.', 'affstackdev-image-manager' ) ) : __( 'Private backup staging is unavailable on this host.', 'affstackdev-image-manager' ) ); self::add( $checks, 'zip', __( 'Portable ZIP backup', 'affstackdev-image-manager' ), ! empty( $backup['zip'] ) ? 'pass' : 'warn', ! empty( $backup['zip'] ) ? __( 'ZipArchive is available for portable backups.', 'affstackdev-image-manager' ) : __( 'ZipArchive is unavailable; optimization/media organization remain usable, but portable backup creation is unavailable.', 'affstackdev-image-manager' ) ); self::add( $checks, 'aes_zip', __( 'Password-encrypted ZIP backup', 'affstackdev-image-manager' ), ! empty( $backup['encrypted_zip'] ) ? 'pass' : 'info', ! empty( $backup['encrypted_zip'] ) ? __( 'AES-256 ZIP self-test passed.', 'affstackdev-image-manager' ) : __( 'Optional AES-256 ZIP encryption is unavailable; unencrypted private backups remain supported when ZIP/storage are available.', 'affstackdev-image-manager' ) ); $provider = (string) ( $connection['provider'] ?? 'none' ); $remote_detail = 'none' === $provider ? __( 'No remote destination is configured. Local/device downloads remain available.', 'affstackdev-image-manager' ) : ( ! empty( $connection['ready'] ) /* translators: %s: the remote backup destination, such as Google Drive. */ ? sprintf( __( '%s is connected.', 'affstackdev-image-manager' ), (string) ( $connection['label'] ?? __( 'Remote destination', 'affstackdev-image-manager' ) ) ) : sprintf( /* translators: %s: the remote backup destination, such as Google Drive. */ __( '%s is configured but not currently verified.', 'affstackdev-image-manager' ), (string) ( $connection['label'] ?? __( 'Remote destination', 'affstackdev-image-manager' ) ) ) ); self::add( $checks, 'remote_backup', __( 'Customer-owned remote backup', 'affstackdev-image-manager' ), 'none' === $provider ? 'info' : ( ! empty( $connection['ready'] ) ? 'pass' : 'warn' ), $remote_detail ); $large = null !== $media && $media >= 10000; self::add( $checks, 'library_scale', __( 'Media-library paging', 'affstackdev-image-manager' ), 'pass', null === $media ? __( 'Media count is unavailable; Image Manager requests 30 attachment records per Media Library page.', 'affstackdev-image-manager' ) : ( $large /* translators: %s: number of attachment records in the Media Library. */ ? sprintf( _n( '%s attachment records detected. The Image Manager library remains paginated at 30 records per page; large-library mode is active.', '%s attachment records detected. The Image Manager library remains paginated at 30 records per page; large-library mode is active.', $media, 'affstackdev-image-manager' ), number_format_i18n( $media ) ) : sprintf( /* translators: %s: number of attachment records in the Media Library. */ _n( '%s attachment records detected. The Image Manager library remains paginated at 30 records per page.', '%s attachment records detected. The Image Manager library remains paginated at 30 records per page.', $media, 'affstackdev-image-manager' ), number_format_i18n( $media ) ) ) ); $warn = count( array_filter( $checks, static fn( $c ) => 'warn' === $c['status'] ) ); return array( 'report' => 'affstackdev-image-manager-release-health', 'report_version' => 1, 'plugin_version' => VERSION, 'generated_utc' => gmdate( 'c' ), 'ready_for_core_workflow' => ! empty( $pre['single_site_supported'] ) && ! empty( $pre['html_api'] ) && ! empty( $pre['storage_ready'] ) && $codec_available, 'warnings' => $warn, 'checks' => $checks, 'inventory' => array( 'attachments' => $media, 'folders' => $folders, 'collections' => $collections, 'optimizer_states' => Repository::counts(), 'local_backup_records' => count( Backup::index() ), 'remote_backup_records' => count( Remote_Backup::index() ), ), 'environment' => array( 'php' => PHP_VERSION, 'php_64bit' => PHP_INT_SIZE >= 8, 'wordpress' => get_bloginfo( 'version' ), 'woocommerce' => defined( 'WC_VERSION' ) ? WC_VERSION : 'not active', 'zip_archive' => class_exists( '\\ZipArchive' ), 'openssl' => function_exists( 'openssl_encrypt' ) && function_exists( 'openssl_decrypt' ), 'gd' => extension_loaded( 'gd' ), 'imagick' => extension_loaded( 'imagick' ), 'memory_budget_bytes' => Settings::memory_budget(), ), 'claims' => array( 'live_browser_delivery_certified' => false, 'remote_provider_certified_by_fixture_only' => true, 'marketplace_approval' => false, 'multisite_supported' => false, ), ); } }