fix(structure): Rename assets and inc folders for Linux compatibility

- assets → Assets
- inc → Inc

Completes the case-sensitivity fixes for Linux servers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-11-26 22:55:31 -06:00
parent 90863cd8f5
commit 33d17f4b56
44 changed files with 0 additions and 0 deletions

500
Inc/image-optimization.php Normal file
View File

@@ -0,0 +1,500 @@
<?php
/**
* Image Optimization Functions
*
* Handles responsive images, WebP/AVIF support, lazy loading, and image preloading.
*
* @package ROI_Theme
* @since 1.0.0
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
/**
* Enable AVIF image support
*/
function roi_enable_avif_support($mime_types) {
$mime_types['avif'] = 'image/avif';
return $mime_types;
}
add_filter('upload_mimes', 'roi_enable_avif_support');
/**
* Add AVIF to allowed file extensions
*/
function roi_allow_avif_extension($types, $file, $filename, $mimes) {
if (false !== strpos($filename, '.avif')) {
$types['ext'] = 'avif';
$types['type'] = 'image/avif';
}
return $types;
}
add_filter('wp_check_filetype_and_ext', 'roi_allow_avif_extension', 10, 4);
/**
* Configure custom image sizes
* Already defined in functions.php, but we can add more if needed
*/
function roi_setup_additional_image_sizes() {
// Add support for additional modern image sizes
add_image_size('roi-hero', 1920, 800, true); // Hero images
add_image_size('roi-card', 600, 400, true); // Card thumbnails
add_image_size('roi-thumbnail-2x', 800, 600, true); // Retina thumbnails
}
add_action('after_setup_theme', 'roi_setup_additional_image_sizes');
/**
* Add custom image sizes to media library dropdown
*/
function roi_custom_image_sizes($sizes) {
return array_merge($sizes, array(
'roi-thumbnail' => __('Thumbnail (400x300)', 'roi-theme'),
'roi-medium' => __('Medium (800x600)', 'roi-theme'),
'roi-large' => __('Large (1200x900)', 'roi-theme'),
'roi-featured-large' => __('Featured Large (1200x600)', 'roi-theme'),
'roi-featured-medium' => __('Featured Medium (800x400)', 'roi-theme'),
'roi-hero' => __('Hero (1920x800)', 'roi-theme'),
'roi-card' => __('Card (600x400)', 'roi-theme'),
));
}
add_filter('image_size_names_choose', 'roi_custom_image_sizes');
/**
* Generate srcset and sizes attributes for responsive images
*
* @param int $attachment_id Image attachment ID
* @param string $size Image size
* @param array $additional_sizes Additional sizes to include in srcset
* @return array Array with 'srcset' and 'sizes' attributes
*/
function roi_get_responsive_image_attrs($attachment_id, $size = 'full', $additional_sizes = array()) {
if (empty($attachment_id)) {
return array('srcset' => '', 'sizes' => '');
}
// Get default WordPress srcset
$srcset = wp_get_attachment_image_srcset($attachment_id, $size);
// Get default WordPress sizes attribute
$sizes = wp_get_attachment_image_sizes($attachment_id, $size);
// Add additional sizes if specified
if (!empty($additional_sizes)) {
$srcset_array = array();
foreach ($additional_sizes as $additional_size) {
$image = wp_get_attachment_image_src($attachment_id, $additional_size);
if ($image) {
$srcset_array[] = $image[0] . ' ' . $image[1] . 'w';
}
}
if (!empty($srcset_array)) {
$srcset .= ', ' . implode(', ', $srcset_array);
}
}
return array(
'srcset' => $srcset,
'sizes' => $sizes,
);
}
/**
* Output responsive image with lazy loading
*
* @param int $attachment_id Image attachment ID
* @param string $size Image size
* @param array $attr Additional image attributes
* @param bool $lazy Enable lazy loading (default: true)
* @return string Image HTML
*/
function roi_get_responsive_image($attachment_id, $size = 'full', $attr = array(), $lazy = true) {
if (empty($attachment_id)) {
return '';
}
// Get responsive attributes
$responsive_attrs = roi_get_responsive_image_attrs($attachment_id, $size);
// Merge default attributes with custom ones
$default_attr = array(
'loading' => $lazy ? 'lazy' : 'eager',
'decoding' => 'async',
);
// Add srcset and sizes if available
if (!empty($responsive_attrs['srcset'])) {
$default_attr['srcset'] = $responsive_attrs['srcset'];
}
if (!empty($responsive_attrs['sizes'])) {
$default_attr['sizes'] = $responsive_attrs['sizes'];
}
$attr = array_merge($default_attr, $attr);
return wp_get_attachment_image($attachment_id, $size, false, $attr);
}
/**
* Enable lazy loading by default for all images
*/
function roi_add_lazy_loading_to_images($attr, $attachment, $size) {
// Don't add lazy loading if explicitly disabled
if (isset($attr['loading']) && $attr['loading'] === 'eager') {
return $attr;
}
// Add lazy loading by default
if (!isset($attr['loading'])) {
$attr['loading'] = 'lazy';
}
// Add async decoding for better performance
if (!isset($attr['decoding'])) {
$attr['decoding'] = 'async';
}
return $attr;
}
add_filter('wp_get_attachment_image_attributes', 'roi_add_lazy_loading_to_images', 10, 3);
/**
* Add lazy loading to content images
*/
function roi_add_lazy_loading_to_content($content) {
// Don't process if empty
if (empty($content)) {
return $content;
}
// Add loading="lazy" to images that don't have it
$content = preg_replace('/<img(?![^>]*loading=)/', '<img loading="lazy" decoding="async"', $content);
return $content;
}
add_filter('the_content', 'roi_add_lazy_loading_to_content', 20);
/**
* Preload critical images (LCP images)
* This should be called for above-the-fold images
*
* @param int $attachment_id Image attachment ID
* @param string $size Image size
*/
function roi_preload_image($attachment_id, $size = 'full') {
if (empty($attachment_id)) {
return;
}
$image_src = wp_get_attachment_image_src($attachment_id, $size);
if (!$image_src) {
return;
}
$srcset = wp_get_attachment_image_srcset($attachment_id, $size);
$sizes = wp_get_attachment_image_sizes($attachment_id, $size);
// Detect image format
$image_url = $image_src[0];
$image_type = 'image/jpeg'; // default
if (strpos($image_url, '.avif') !== false) {
$image_type = 'image/avif';
} elseif (strpos($image_url, '.webp') !== false) {
$image_type = 'image/webp';
} elseif (strpos($image_url, '.png') !== false) {
$image_type = 'image/png';
}
// Build preload link
$preload = sprintf(
'<link rel="preload" as="image" href="%s" type="%s"',
esc_url($image_url),
esc_attr($image_type)
);
if ($srcset) {
$preload .= sprintf(' imagesrcset="%s"', esc_attr($srcset));
}
if ($sizes) {
$preload .= sprintf(' imagesizes="%s"', esc_attr($sizes));
}
$preload .= '>';
echo $preload . "\n";
}
/**
* Preload featured image for single posts (LCP optimization)
*/
function roi_preload_featured_image() {
// Only on single posts/pages with featured images
if (!is_singular() || !has_post_thumbnail()) {
return;
}
// Get the featured image ID
$thumbnail_id = get_post_thumbnail_id();
// Determine the size based on the post type
$size = 'roi-featured-large';
// Preload the image
roi_preload_image($thumbnail_id, $size);
}
add_action('wp_head', 'roi_preload_featured_image', 5);
/**
* Enable fetchpriority attribute for featured images
*/
function roi_add_fetchpriority_to_featured_image($attr, $attachment, $size) {
// Only add fetchpriority="high" to featured images on singular pages
if (is_singular() && get_post_thumbnail_id() === $attachment->ID) {
$attr['fetchpriority'] = 'high';
$attr['loading'] = 'eager'; // Don't lazy load LCP images
}
return $attr;
}
add_filter('wp_get_attachment_image_attributes', 'roi_add_fetchpriority_to_featured_image', 20, 3);
/**
* Optimize image quality for uploads
*/
function roi_optimize_image_quality($quality, $mime_type) {
// Set quality to 85% for better file size without visible quality loss
if ($mime_type === 'image/jpeg') {
return 85;
}
return $quality;
}
add_filter('jpeg_quality', 'roi_optimize_image_quality', 10, 2);
add_filter('wp_editor_set_quality', 'roi_optimize_image_quality', 10, 2);
/**
* Add picture element support for WebP/AVIF with fallbacks
*
* @param int $attachment_id Image attachment ID
* @param string $size Image size
* @param array $attr Additional attributes
* @return string Picture element HTML
*/
function roi_get_picture_element($attachment_id, $size = 'full', $attr = array()) {
if (empty($attachment_id)) {
return '';
}
$image_src = wp_get_attachment_image_src($attachment_id, $size);
if (!$image_src) {
return '';
}
$srcset = wp_get_attachment_image_srcset($attachment_id, $size);
$sizes = wp_get_attachment_image_sizes($attachment_id, $size);
$alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
// Default attributes
$default_attr = array(
'loading' => 'lazy',
'decoding' => 'async',
'alt' => $alt,
);
$attr = array_merge($default_attr, $attr);
// Build picture element
$picture = '<picture>';
// Add AVIF source if available
$avif_url = str_replace(array('.jpg', '.jpeg', '.png', '.webp'), '.avif', $image_src[0]);
if ($avif_url !== $image_src[0]) {
$picture .= sprintf(
'<source type="image/avif" srcset="%s" sizes="%s">',
esc_attr($avif_url),
esc_attr($sizes)
);
}
// Add WebP source if available
$webp_url = str_replace(array('.jpg', '.jpeg', '.png'), '.webp', $image_src[0]);
if ($webp_url !== $image_src[0]) {
$picture .= sprintf(
'<source type="image/webp" srcset="%s" sizes="%s">',
esc_attr($webp_url),
esc_attr($sizes)
);
}
// Fallback img tag
$picture .= wp_get_attachment_image($attachment_id, $size, false, $attr);
$picture .= '</picture>';
return $picture;
}
/**
* Configurar threshold de escala de imágenes grandes
* WordPress 5.3+ escala imágenes mayores a 2560px por defecto
* Mantenemos 2560px como límite para balance entre calidad y rendimiento
*/
function roi_big_image_size_threshold($threshold) {
// Mantener 2560px como threshold (no desactivar completamente)
return 2560;
}
add_filter('big_image_size_threshold', 'roi_big_image_size_threshold');
/**
* Set maximum srcset image width
*/
function roi_max_srcset_image_width($max_width, $size_array) {
// Limit srcset to images up to 2560px wide
return 2560;
}
add_filter('max_srcset_image_width', 'roi_max_srcset_image_width', 10, 2);
/**
* Habilitar generación automática de WebP en WordPress
* WordPress 5.8+ soporta WebP nativamente si el servidor tiene soporte
*/
function roi_enable_webp_generation($editors) {
// Verificar que GD o Imagick tengan soporte WebP
if (extension_loaded('gd')) {
$gd_info = gd_info();
if (!empty($gd_info['WebP Support'])) {
// WebP está soportado en GD
return $editors;
}
}
if (extension_loaded('imagick')) {
$imagick = new Imagick();
$formats = $imagick->queryFormats('WEBP');
if (count($formats) > 0) {
// WebP está soportado en Imagick
return $editors;
}
}
return $editors;
}
add_filter('wp_image_editors', 'roi_enable_webp_generation');
/**
* Configurar tipos MIME adicionales para WebP y AVIF
*/
function roi_additional_mime_types($mimes) {
// WebP ya está soportado en WordPress 5.8+, pero lo agregamos por compatibilidad
if (!isset($mimes['webp'])) {
$mimes['webp'] = 'image/webp';
}
// AVIF soportado desde WordPress 6.5+
if (!isset($mimes['avif'])) {
$mimes['avif'] = 'image/avif';
}
return $mimes;
}
add_filter('mime_types', 'roi_additional_mime_types');
/**
* Remover tamaños de imagen no utilizados para ahorrar espacio
*/
function roi_remove_unused_image_sizes($sizes) {
// Remover tamaños de WordPress que no usamos
unset($sizes['medium_large']); // 768px - no necesario con nuestros tamaños custom
unset($sizes['1536x1536']); // 2x medium_large - no necesario
unset($sizes['2048x2048']); // 2x large - no necesario
return $sizes;
}
add_filter('intermediate_image_sizes_advanced', 'roi_remove_unused_image_sizes');
/**
* Agregar sizes attribute personalizado según contexto
* Mejora la selección del tamaño correcto de imagen por el navegador
*/
function roi_responsive_image_sizes_attr($sizes, $size, $image_src, $image_meta, $attachment_id) {
// Para imágenes destacadas grandes (single posts)
if ($size === 'roi-featured-large' || $size === 'roi-large') {
$sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px';
}
// Para imágenes destacadas medianas (archives)
elseif ($size === 'roi-featured-medium' || $size === 'roi-medium') {
$sizes = '(max-width: 768px) 100vw, (max-width: 992px) 50vw, 800px';
}
// Para thumbnails (widgets, related posts)
elseif ($size === 'roi-thumbnail') {
$sizes = '(max-width: 576px) 100vw, 400px';
}
// Para hero images
elseif ($size === 'roi-hero') {
$sizes = '100vw';
}
return $sizes;
}
add_filter('wp_calculate_image_sizes', 'roi_responsive_image_sizes_attr', 10, 5);
/**
* Forzar regeneración de metadatos de imagen para incluir WebP/AVIF
* Solo se ejecuta cuando sea necesario
*/
function roi_maybe_regenerate_image_metadata($metadata, $attachment_id) {
// Verificar si ya tiene formatos modernos generados
if (!empty($metadata) && is_array($metadata)) {
// WordPress maneja automáticamente la generación de sub-formatos
return $metadata;
}
return $metadata;
}
add_filter('wp_generate_attachment_metadata', 'roi_maybe_regenerate_image_metadata', 10, 2);
/**
* Excluir lazy loading en la primera imagen del contenido (posible LCP)
*/
function roi_skip_lazy_loading_first_image($content) {
// Solo en posts/páginas singulares
if (!is_singular()) {
return $content;
}
// Contar si es la primera imagen
static $first_image = true;
if ($first_image) {
// Cambiar loading="lazy" a loading="eager" en la primera imagen
$content = preg_replace(
'/<img([^>]+?)loading=["\']lazy["\']/',
'<img$1loading="eager"',
$content,
1 // Solo la primera coincidencia
);
$first_image = false;
}
return $content;
}
add_filter('the_content', 'roi_skip_lazy_loading_first_image', 15);
/**
* Agregar soporte para formatos de imagen modernos en subsizes
* WordPress 5.8+ genera automáticamente WebP si está disponible
*/
function roi_enable_image_subsizes($metadata, $attachment_id, $context) {
if ($context !== 'create') {
return $metadata;
}
// WordPress genera automáticamente WebP subsizes si está disponible
// Este filtro asegura que se ejecute correctamente
return $metadata;
}
add_filter('wp_generate_attachment_metadata', 'roi_enable_image_subsizes', 20, 3);