put_contents( $upload_file, $content, FS_CHMOD_FILE ); // 2. Physical copy in site root so AI crawlers and external checkers // can fetch /llms.txt directly without WordPress rewrite rules. $root_file = rtrim( ABSPATH, '/' ) . '/' . $filename; $fs->put_contents( $root_file, $content, FS_CHMOD_FILE ); // 3. Ensure .htaccess declares UTF-8 charset for these files. // Apache serves text/plain as ISO-8859-1 by default; without this // German umlauts and all non-ASCII chars render incorrectly. LLMSTXT_Htaccess::insert(); if ( $ok ) { update_option( 'llmstxt_file_info', array( 'updated' => current_time( 'mysql' ), 'size' => strlen( $content ), 'lines' => substr_count( $content, "\n" ) + 1, ) ); } return (bool) $ok; } /** * Reads the stored llms.txt content (from uploads storage). * * @param string $type 'standard' or 'full'. * @return string File contents or empty string. */ public static function read( string $type = 'standard' ): string { $fs = self::get_fs(); $file = self::get_dir() . self::get_filename( $type ); if ( ! $fs || ! $fs->exists( $file ) ) { return ''; } $contents = $fs->get_contents( $file ); return is_string( $contents ) ? $contents : ''; } /** * Checks whether the llms.txt file exists (uploads storage). * * @param string $type 'standard' or 'full'. */ public static function exists( string $type = 'standard' ): bool { $fs = self::get_fs(); $file = self::get_dir() . self::get_filename( $type ); return $fs && $fs->exists( $file ); } /** * Returns the public URL for the llms.txt file. * * @param string $type 'standard' or 'full'. */ public static function get_url( string $type = 'standard' ): string { return home_url( '/' . self::get_filename( $type ) ); } /** * Deletes the stored llms.txt file(s) from both locations. * * @param string $type 'standard' or 'full'. */ public static function delete( string $type = 'standard' ): void { $fs = self::get_fs(); $filename = self::get_filename( $type ); if ( ! $fs ) { return; } // Remove from uploads storage. $upload_file = self::get_dir() . $filename; if ( $fs->exists( $upload_file ) ) { $fs->delete( $upload_file ); } // Remove physical root copy. $root_file = rtrim( ABSPATH, '/' ) . '/' . $filename; if ( $fs->exists( $root_file ) ) { $fs->delete( $root_file ); } } } /** * Manages the .htaccess charset rule for llms.txt files. */ class LLMSTXT_Htaccess { const MARKER = 'LLMs.txt Generator'; /** * Insert charset rule into the root .htaccess via WordPress markers. * Called on plugin activation and after every write. */ public static function insert(): void { $htaccess = rtrim( ABSPATH, '/' ) . '/.htaccess'; if ( ! function_exists( 'insert_with_markers' ) ) { require_once ABSPATH . 'wp-admin/includes/misc.php'; } $rules = array( '', ' AddDefaultCharset UTF-8', '', ); insert_with_markers( $htaccess, self::MARKER, $rules ); } /** * Remove the charset rule from .htaccess on deactivation/uninstall. */ public static function remove(): void { $htaccess = rtrim( ABSPATH, '/' ) . '/.htaccess'; if ( ! function_exists( 'insert_with_markers' ) ) { require_once ABSPATH . 'wp-admin/includes/misc.php'; } insert_with_markers( $htaccess, self::MARKER, array() ); } }