diff --git a/functions.php b/functions.php index a87370d4..2c533d3c 100644 --- a/functions.php +++ b/functions.php @@ -26,6 +26,8 @@ function () { \BEA\Theme\Framework\Framework::get_container()->boot_services(); } ); + +require_once __DIR__ . '/inc/Helpers/Misc.php'; require_once __DIR__ . '/inc/Helpers/Svg.php'; require_once __DIR__ . '/inc/Helpers/Formatting/Datetime.php'; require_once __DIR__ . '/inc/Helpers/Formatting/Escape.php'; diff --git a/inc/Helpers/Misc.php b/inc/Helpers/Misc.php new file mode 100644 index 00000000..54dfeef9 --- /dev/null +++ b/inc/Helpers/Misc.php @@ -0,0 +1,236 @@ + '', + 'details' => '', + 'details_accessible' => '', + 'href' => '', + 'caption' => '', + 'icon' => get_file_icon( '' ), + ]; + + $file_href = wp_get_attachment_url( $file_id ); + + if ( empty( $file_href ) ) { + wp_cache_set( $file_id, $file_infos, FILE_INFOS_CACHE_GROUP ); + + return $file_infos; + } + + $file_path = get_attached_file( $file_id ); + + if ( empty( $file_path ) ) { + wp_cache_set( $file_id, $file_infos, FILE_INFOS_CACHE_GROUP ); + + return $file_infos; + } + + $file_ext = pathinfo( $file_path, PATHINFO_EXTENSION ); + + if ( empty( $file_ext ) ) { + wp_cache_set( $file_id, $file_infos, FILE_INFOS_CACHE_GROUP ); + + return $file_infos; + } + + $file_bytes = get_attachment_filesize( $file_id, $file_path ); + $file_size = $file_bytes > 0 ? (string) size_format( $file_bytes ) : ''; + $accessible_file_size = $file_bytes > 0 ? get_accessible_file_size_label( $file_bytes ) : ''; + $file_name = (string) ( get_the_title( $file_id ) ?? '' ); + + $file_infos = [ + 'file_name' => $file_name, + 'details' => get_file_detail( $file_name, $file_ext, $file_size ), + 'details_accessible' => get_file_detail( $file_name, $file_ext, $accessible_file_size ), + 'href' => $file_href, + 'caption' => (string) wp_get_attachment_caption( $file_id ), + 'icon' => get_file_icon( $file_ext ), + ]; + + wp_cache_set( $file_id, $file_infos, FILE_INFOS_CACHE_GROUP ); + + return $file_infos; +} + +/** + * Delete cached file infos for an attachment. + * + * @param int $file_id Attachment ID. + */ +function delete_file_infos_cache( int $file_id ): void { + wp_cache_delete( $file_id, FILE_INFOS_CACHE_GROUP ); +} + +/** + * Invalidate file infos cache when attached file path or metadata (incl. filesize) change. + * + * @param mixed $meta_id Meta ID, or list of meta IDs for deleted_post_meta. + * @param int $object_id Object ID. + * @param string $meta_key Meta key. + */ +function delete_file_infos_cache_on_meta_change( $meta_id, $object_id, string $meta_key ): void { + if ( ! in_array( $meta_key, [ '_wp_attachment_metadata', '_wp_attached_file' ], true ) ) { + return; + } + + delete_file_infos_cache( (int) $object_id ); +} + +add_action( 'edit_attachment', __NAMESPACE__ . '\\delete_file_infos_cache' ); +add_action( 'delete_attachment', __NAMESPACE__ . '\\delete_file_infos_cache' ); +add_action( 'added_post_meta', __NAMESPACE__ . '\\delete_file_infos_cache_on_meta_change', 10, 3 ); +add_action( 'updated_post_meta', __NAMESPACE__ . '\\delete_file_infos_cache_on_meta_change', 10, 3 ); +add_action( 'deleted_post_meta', __NAMESPACE__ . '\\delete_file_infos_cache_on_meta_change', 10, 3 ); + +/** + * Resolve attachment filesize in bytes from metadata, with wp_filesize() fallback and meta backfill. + * + * @param int $file_id Attachment ID. + * @param string $file_path Absolute path to the attached file. + * + * @return int Filesize in bytes, or 0 when unavailable. + */ +function get_attachment_filesize( int $file_id, string $file_path ): int { + $metadata = wp_get_attachment_metadata( $file_id ); + + if ( is_array( $metadata ) && ! empty( $metadata['filesize'] ) ) { + return (int) $metadata['filesize']; + } + + $filesize = wp_filesize( $file_path ); + + if ( $filesize <= 0 ) { + return 0; + } + + if ( ! is_array( $metadata ) ) { + $metadata = []; + } + + $metadata['filesize'] = $filesize; + wp_update_attachment_metadata( $file_id, $metadata ); + + return $filesize; +} + +/** + * Get file details + * + * @param string $file_name File display name. + * @param string $file_ext File extension. + * @param string $file_size Formatted file size label. + * + * @return string $file_detail + */ +function get_file_detail( string $file_name, string $file_ext, string $file_size ): string { + $details = []; + + if ( ! empty( $file_name ) ) { + $details[] = $file_name; + } + + if ( ! empty( $file_ext ) ) { + $details[] = strtoupper( $file_ext ); + } + + if ( ! empty( $file_size ) ) { + $details[] = $file_size; + } + + return implode( ' – ', $details ); +} + +/** + * Get accessible file size label from raw bytes (screen-reader friendly, pluralized units). + * + * Mirrors size_format() unit thresholds without parsing its localized output. + * + * @param int $bytes File size in bytes. + * + * @return string + */ +function get_accessible_file_size_label( int $bytes ): string { + if ( $bytes <= 0 ) { + return ''; + } + + $quant = [ + 'tb' => TB_IN_BYTES, + 'gb' => GB_IN_BYTES, + 'mb' => MB_IN_BYTES, + 'kb' => KB_IN_BYTES, + 'b' => 1, + ]; + + $unit = 'b'; + $mag = 1; + + foreach ( $quant as $unit_key => $unit_mag ) { + if ( (float) $bytes >= $unit_mag ) { + $unit = $unit_key; + $mag = $unit_mag; + break; + } + } + + $value = $bytes / $mag; + $int_value = (int) round( $value ); + $formatted = number_format_i18n( $value, 0 ); + + /* translators: file size units (byte, kilobyte, megabyte, etc.) */ + $unit_label = match ( $unit ) { + 'b' => _n( 'byte', 'bytes', $int_value, 'beapi-frontend-framework' ), + 'kb' => _n( 'kilobyte', 'kilobytes', $int_value, 'beapi-frontend-framework' ), + 'mb' => _n( 'megabyte', 'megabytes', $int_value, 'beapi-frontend-framework' ), + 'gb' => _n( 'gigabyte', 'gigabytes', $int_value, 'beapi-frontend-framework' ), + 'tb' => _n( 'terabyte', 'terabytes', $int_value, 'beapi-frontend-framework' ), + default => null, + }; + + if ( null === $unit_label ) { + return ''; + } + + return $formatted . ' ' . $unit_label; +} + +/** + * @param string $file_ext File extension. + * + * @return string + */ +function get_file_icon( string $file_ext ): string { + $file_icon = 'file'; + + if ( in_array( strtolower( $file_ext ), [ 'jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'svg', 'bmp', 'ico' ], true ) ) { + $file_icon = 'file-image'; + } + + return $file_icon; +} diff --git a/src/img/icons/sprite/file-image.svg b/src/img/icons/sprite/file-image.svg new file mode 100644 index 00000000..973318f4 --- /dev/null +++ b/src/img/icons/sprite/file-image.svg @@ -0,0 +1 @@ + diff --git a/src/img/icons/sprite/file.svg b/src/img/icons/sprite/file.svg new file mode 100644 index 00000000..b9469573 --- /dev/null +++ b/src/img/icons/sprite/file.svg @@ -0,0 +1 @@ +