*/ public static function problems(): array { $problems = Sanitizer::environment_problems(); if ( ! Storage::location()->is_usable() ) { $problems[] = self::STORAGE_UNAVAILABLE; } $max = defined( 'WP_MAX_MEMORY_LIMIT' ) ? (string) WP_MAX_MEMORY_LIMIT : ''; if ( ! self::memory_ok( (string) ini_get( 'memory_limit' ), $max, memory_get_usage( true ) ) ) { $problems[] = self::MEMORY_LOW; } if ( OpenAiKey::unreadable() ) { $problems[] = self::KEY_UNREADABLE; } elseif ( null === Settings::provider_id() ) { $problems[] = self::NO_PROVIDER; } return $problems; } /** * Whether try-on can be offered on this server. */ public static function is_ready(): bool { return array() === self::problems(); } /** * Whether a phone photo fits in memory once WordPress raises the limit for image work * (wp_raise_memory_limit( 'image' ) goes up to WP_MAX_MEMORY_LIMIT). * * @param string $memory_limit php.ini memory_limit. * @param string $wp_max WP_MAX_MEMORY_LIMIT, or '' when not defined. * @param int $usage Bytes already in use. */ public static function memory_ok( string $memory_limit, string $wp_max, int $usage ): bool { $limit = MemoryBudget::to_bytes( $memory_limit ); $raised = '' === $wp_max ? $limit : MemoryBudget::to_bytes( $wp_max ); if ( null === $limit || null === $raised ) { return true; } $budget = new MemoryBudget( max( $limit, $raised ) - $usage ); return $budget->fits( self::PHONE_PHOTO[0], self::PHONE_PHOTO[1], Sanitizer::MAX_SIDE ); } /** * Whether the only thing missing is the owner's own setup (no AI key yet), as on a fresh install. * That is a next step, not an error. * * @param string[] $problems Problem codes. */ public static function only_setup_left( array $problems ): bool { return array( self::NO_PROVIDER ) === $problems; } /** * What the owner should do about a problem. * * @param string $problem Problem code. */ public static function message( string $problem ): string { switch ( $problem ) { case 'gd_missing': return __( 'The PHP GD extension is not installed. Ask your host to enable it.', 'fitzaro' ); case 'gd_jpeg_missing': return __( 'The PHP GD extension was built without JPEG support. Ask your host to enable it.', 'fitzaro' ); case 'gd_png_missing': return __( 'The PHP GD extension was built without PNG support. Ask your host to enable it.', 'fitzaro' ); case self::STORAGE_UNAVAILABLE: return __( 'Fitzaro cannot create a private folder for shopper photos. Make the uploads folder writable, or define FITZARO_STORAGE_DIR in wp-config.php.', 'fitzaro' ); case self::NO_PROVIDER: return __( 'No AI provider is set up. Add your OpenAI API key in WooCommerce → Settings → Fitzaro.', 'fitzaro' ); case self::KEY_UNREADABLE: return __( 'The saved OpenAI key can no longer be read because the site\'s security keys changed. Enter it again in WooCommerce → Settings → Fitzaro.', 'fitzaro' ); case self::MEMORY_LOW: return __( 'PHP has too little memory to process a phone photo. Raise memory_limit or WP_MAX_MEMORY_LIMIT to at least 256M.', 'fitzaro' ); default: return $problem; } } /** * Registers the Site Health test. * * @param mixed $tests Site Health tests; another plugin's filter may have broken the value. * @return array */ public static function add_site_health_test( $tests ): array { $tests = is_array( $tests ) ? $tests : array(); $tests['direct']['fitzaro'] = array( 'label' => __( 'Fitzaro virtual try-on requirements', 'fitzaro' ), 'test' => array( self::class, 'site_health_result' ), ); return $tests; } /** * Site Health result. * * @return array */ public static function site_health_result(): array { $problems = self::problems(); $badge = array( 'label' => __( 'Fitzaro', 'fitzaro' ), 'color' => 'blue', ); if ( array() === $problems ) { $location = Storage::location(); return array( 'label' => __( 'Fitzaro can run virtual try-on', 'fitzaro' ), 'status' => 'good', 'badge' => $badge, 'description' => '

' . esc_html( $location->is_outside_web_root() ? __( 'Shopper photos are stored outside the public web folder and served only through signed links.', 'fitzaro' ) : __( 'Shopper photos are stored in a protected folder and served only through signed links.', 'fitzaro' ) ) . '

', 'test' => 'fitzaro', ); } $items = ''; foreach ( $problems as $problem ) { $items .= '
  • ' . esc_html( self::message( $problem ) ) . '
  • '; } return array( 'label' => __( 'Fitzaro virtual try-on is turned off', 'fitzaro' ), 'status' => 'critical', 'badge' => $badge, 'description' => '

    ' . esc_html__( 'Shoppers do not see the try-on button until these are fixed:', 'fitzaro' ) . '

    ', 'test' => 'fitzaro', ); } /** * Shows the problems on Fitzaro's own settings tab only (NoticePlace); Site Health shows them too. */ public static function render_admin_notice(): void { if ( ! current_user_can( 'manage_woocommerce' ) || Admin\NoticePlace::SETTINGS !== Admin\NoticePlace::current() ) { return; } $problems = self::problems(); if ( array() === $problems ) { return; } if ( self::only_setup_left( $problems ) ) { echo '

    ' . esc_html__( 'Finish setting up Fitzaro', 'fitzaro' ) . '

    ' . esc_html__( 'Add your OpenAI API key below. The Try it on button appears on product pages once the key is saved.', 'fitzaro' ) . '

    '; return; } echo '

    ' . esc_html__( 'Fitzaro virtual try-on is turned off.', 'fitzaro' ) . '

      '; foreach ( $problems as $problem ) { echo '
    • ' . esc_html( self::message( $problem ) ) . '
    • '; } echo '
    '; } }