This commit is contained in:
2025-03-05 10:58:57 +01:00
parent 385348c310
commit 5d7a17bd4c

View File

@@ -1,12 +1,12 @@
<?php <?php
/* /*
Plugin Name: WP Thumbor Integration Plugin Name: WP Thumbor Integration
Description: Intègre Thumbor dans WordPress en transformant les URLs des images présentes dans le <body> (contenu, header, etc.). L'URL de Thumbor, les filtres et la clé secrète sont configurables. Les images sont redimensionnées si une taille est spécifiée dans le nom du fichier ou dans les attributs HTML. La transformation est désactivée dans certains cas (ex. Optimize More) pour éviter des erreurs getimagesize(). Le favicon et les images en dehors du <body> ne sont pas modifiés. Description: Intègre Thumbor dans WordPress en transformant les URLs des images présentes dans le <body> (contenu, header, backgrounds, etc.). L'URL de Thumbor, les filtres et la clé secrète sont configurables. Les images sont redimensionnées si une taille est spécifiée dans le nom du fichier ou dans les attributs HTML. La transformation est désactivée dans certains cas (ex. Optimize More) pour éviter des erreurs getimagesize(). Le favicon, les scripts et les images en dehors du <body> ne sont pas modifiés.
Version: 1.4 Version: 1.5
Author: Votre Nom Author: MrRaph_
*/ */
if (!defined('ABSPATH')) { if ( ! defined('ABSPATH') ) {
exit; // Sécurisation exit; // Sécurisation
} }
@@ -31,8 +31,8 @@ function wp_thumbor_get_options() {
*/ */
function wp_thumbor_should_skip_transform() { function wp_thumbor_should_skip_transform() {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
foreach ($trace as $frame) { foreach ( $trace as $frame ) {
if (isset($frame['file']) && stripos($frame['file'], 'optimize-more') !== false) { if ( isset($frame['file']) && stripos($frame['file'], 'optimize-more') !== false ) {
return true; return true;
} }
} }
@@ -57,11 +57,11 @@ add_action('admin_menu', 'wp_thumbor_admin_menu');
* Affiche la page de réglages du plugin dans l'administration. * Affiche la page de réglages du plugin dans l'administration.
*/ */
function wp_thumbor_settings_page() { function wp_thumbor_settings_page() {
if (!current_user_can('manage_options')) { if ( ! current_user_can('manage_options') ) {
wp_die(__('Vous n\'avez pas l\'autorisation d\'accéder à cette page.')); wp_die(__('Vous n\'avez pas l\'autorisation d\'accéder à cette page.'));
} }
if (isset($_POST['wp_thumbor_nonce']) && wp_verify_nonce($_POST['wp_thumbor_nonce'], 'wp_thumbor_save_settings')) { if ( isset($_POST['wp_thumbor_nonce']) && wp_verify_nonce($_POST['wp_thumbor_nonce'], 'wp_thumbor_save_settings') ) {
$options = array(); $options = array();
$options['thumbor_base_url'] = isset($_POST['thumbor_base_url']) ? esc_url_raw(trim($_POST['thumbor_base_url'])) : ''; $options['thumbor_base_url'] = isset($_POST['thumbor_base_url']) ? esc_url_raw(trim($_POST['thumbor_base_url'])) : '';
$options['thumbor_filters'] = isset($_POST['thumbor_filters']) ? sanitize_text_field(trim($_POST['thumbor_filters'])) : ''; $options['thumbor_filters'] = isset($_POST['thumbor_filters']) ? sanitize_text_field(trim($_POST['thumbor_filters'])) : '';
@@ -112,8 +112,8 @@ function wp_thumbor_settings_page() {
* @return array|false Tableau avec 'width' et 'height' ou false si aucun pattern n'est trouvé. * @return array|false Tableau avec 'width' et 'height' ou false si aucun pattern n'est trouvé.
*/ */
function wp_thumbor_extract_dimensions_from_url($url) { function wp_thumbor_extract_dimensions_from_url($url) {
if (preg_match('/-([0-9]+)x([0-9]+)(?=\.(jpg|jpeg|png|gif|webp)$)/i', $url, $matches)) { if ( preg_match('/-([0-9]+)x([0-9]+)(?=\.(jpg|jpeg|png|gif|webp)$)/i', $url, $matches) ) {
return array('width' => intval($matches[1]), 'height' => intval($matches[2])); return array( 'width' => intval($matches[1]), 'height' => intval($matches[2]) );
} }
return false; return false;
} }
@@ -134,40 +134,40 @@ function wp_thumbor_extract_dimensions_from_url($url) {
*/ */
function wp_thumbor_transform_url($url, $width = null, $height = null) { function wp_thumbor_transform_url($url, $width = null, $height = null) {
// Ne pas transformer dans l'administration // Ne pas transformer dans l'administration
if (is_admin()) { if ( is_admin() ) {
return $url; return $url;
} }
// Ignorer le fichier favicon.ico // Ignorer le fichier favicon.ico
if (basename(parse_url($url, PHP_URL_PATH)) === 'favicon.ico') { if ( basename(parse_url($url, PHP_URL_PATH)) === 'favicon.ico' ) {
return $url; return $url;
} }
if (wp_thumbor_should_skip_transform()) { if ( wp_thumbor_should_skip_transform() ) {
return $url; return $url;
} }
$options = wp_thumbor_get_options(); $options = wp_thumbor_get_options();
$thumbor_base_url = isset($options['thumbor_base_url']) ? rtrim($options['thumbor_base_url'], '/') : ''; $thumbor_base_url = isset($options['thumbor_base_url']) ? rtrim($options['thumbor_base_url'], '/') : '';
if (empty($thumbor_base_url)) { if ( empty($thumbor_base_url) ) {
return $url; return $url;
} }
// Évite une double transformation // Évite une double transformation
if (strpos($url, $thumbor_base_url) === 0) { if ( strpos($url, $thumbor_base_url) === 0 ) {
return $url; return $url;
} }
// Si aucune dimension n'est fournie via les attributs, tenter d'extraire depuis le nom du fichier // Si aucune dimension n'est fournie via les attributs, tenter d'extraire depuis le nom du fichier
if (empty($width) && empty($height)) { if ( empty($width) && empty($height) ) {
$dimensions = wp_thumbor_extract_dimensions_from_url($url); $dimensions = wp_thumbor_extract_dimensions_from_url($url);
if ($dimensions) { if ( $dimensions ) {
$width = $dimensions['width']; $width = $dimensions['width'];
$height = $dimensions['height']; $height = $dimensions['height'];
} }
} }
// Préparation de la partie redimensionnement // Préparation de la partie redimensionnement
if ($width || $height) { if ( $width || $height ) {
$w = $width ? $width : 0; $w = $width ? $width : 0;
$h = $height ? $height : 0; $h = $height ? $height : 0;
$resize = $w . 'x' . $h . '/'; $resize = $w . 'x' . $h . '/';
@@ -177,7 +177,7 @@ function wp_thumbor_transform_url($url, $width = null, $height = null) {
// Préparation de la partie filtres à partir des réglages // Préparation de la partie filtres à partir des réglages
$thumbor_filters = isset($options['thumbor_filters']) ? trim($options['thumbor_filters']) : ''; $thumbor_filters = isset($options['thumbor_filters']) ? trim($options['thumbor_filters']) : '';
if (!empty($thumbor_filters)) { if ( ! empty($thumbor_filters) ) {
// Utilise "/" comme séparateur attendu par Thumbor. // Utilise "/" comme séparateur attendu par Thumbor.
$filterPart = 'filters:' . $thumbor_filters . '/'; $filterPart = 'filters:' . $thumbor_filters . '/';
} else { } else {
@@ -188,7 +188,7 @@ function wp_thumbor_transform_url($url, $width = null, $height = null) {
$path_to_sign = $resize . $filterPart . $url; $path_to_sign = $resize . $filterPart . $url;
// Détermine l'endpoint : si une clé secrète est renseignée, on signe, sinon on utilise "unsafe" // Détermine l'endpoint : si une clé secrète est renseignée, on signe, sinon on utilise "unsafe"
if (!empty($options['thumbor_secret_key'])) { if ( ! empty($options['thumbor_secret_key']) ) {
$hmac = hash_hmac('sha1', $path_to_sign, $options['thumbor_secret_key'], true); $hmac = hash_hmac('sha1', $path_to_sign, $options['thumbor_secret_key'], true);
$encoded_signature = strtr(base64_encode($hmac), '+/', '-_'); $encoded_signature = strtr(base64_encode($hmac), '+/', '-_');
$endpoint = $encoded_signature; $endpoint = $encoded_signature;
@@ -199,113 +199,46 @@ function wp_thumbor_transform_url($url, $width = null, $height = null) {
return $thumbor_base_url . '/' . $endpoint . '/' . $path_to_sign; return $thumbor_base_url . '/' . $endpoint . '/' . $path_to_sign;
} }
/**
* Filtre le contenu des articles (the_content) pour transformer les images.
* Cette fonction travaille sur le HTML complet.
*/
function wp_thumbor_filter_content_images($content) {
if (is_admin()) {
return $content;
}
if (empty($content)) {
return $content;
}
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$html = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
if (empty($html)) {
return $content;
}
$dom->loadHTML($html);
$images = $dom->getElementsByTagName('img');
foreach ($images as $img) {
$width = $img->getAttribute('width');
$height = $img->getAttribute('height');
$width = $width ? intval($width) : null;
$height = $height ? intval($height) : null;
if ($img->hasAttribute('src') && preg_match('/^https?:\/\//', $img->getAttribute('src'))) {
$img->setAttribute('src', wp_thumbor_transform_url($img->getAttribute('src'), $width, $height));
}
if ($img->hasAttribute('data-src') && preg_match('/^https?:\/\//', $img->getAttribute('data-src'))) {
$img->setAttribute('data-src', wp_thumbor_transform_url($img->getAttribute('data-src'), $width, $height));
}
if ($img->hasAttribute('srcset')) {
$srcsetItems = explode(',', $img->getAttribute('srcset'));
$newSrcset = array();
foreach ($srcsetItems as $item) {
$parts = preg_split('/\s+/', trim($item));
if (!empty($parts[0]) && preg_match('/^https?:\/\//', $parts[0])) {
$newURL = wp_thumbor_transform_url($parts[0]);
$newSrcset[] = isset($parts[1]) ? $newURL . ' ' . $parts[1] : $newURL;
} else {
$newSrcset[] = $item;
}
}
$img->setAttribute('srcset', implode(', ', $newSrcset));
}
if ($img->hasAttribute('data-srcset')) {
$srcsetItems = explode(',', $img->getAttribute('data-srcset'));
$newSrcset = array();
foreach ($srcsetItems as $item) {
$parts = preg_split('/\s+/', trim($item));
if (!empty($parts[0]) && preg_match('/^https?:\/\//', $parts[0])) {
$newURL = wp_thumbor_transform_url($parts[0]);
$newSrcset[] = isset($parts[1]) ? $newURL . ' ' . $parts[1] : $newURL;
} else {
$newSrcset[] = $item;
}
}
$img->setAttribute('data-srcset', implode(', ', $newSrcset));
}
}
// Renvoie le document HTML complet (head, body, etc.)
return $dom->saveHTML();
}
add_filter('the_content', 'wp_thumbor_filter_content_images');
/**
* Démarre la mise en tampon de la sortie pour traiter toutes les images présentes dans le HTML final.
*/
function wp_thumbor_start_output_buffer() {
if (!is_admin() && !defined('DOING_AJAX')) {
ob_start('wp_thumbor_output_buffer_callback');
}
}
add_action('template_redirect', 'wp_thumbor_start_output_buffer');
/** /**
* Callback de traitement de la sortie (output buffer). * Callback de traitement de la sortie (output buffer).
* Parcourt le document HTML complet et transforme les URLs de toutes les balises <img>. * Parcourt le document HTML complet et transforme :
* - Les URLs des balises <img>
* - Les URLs en background définies dans le style inline
* - Les attributs data-image et data-src utilisés pour les backgrounds
*
* Les balises <script> sont ignorées.
*/ */
function wp_thumbor_output_buffer_callback($buffer) { function wp_thumbor_output_buffer_callback($buffer) {
if (empty($buffer)) { if ( empty($buffer) ) {
return $buffer; return $buffer;
} }
libxml_use_internal_errors(true); libxml_use_internal_errors(true);
$dom = new DOMDocument(); $dom = new DOMDocument();
$html = mb_convert_encoding($buffer, 'HTML-ENTITIES', 'UTF-8'); $html = mb_convert_encoding($buffer, 'HTML-ENTITIES', 'UTF-8');
if (empty($html)) { if ( empty($html) ) {
return $buffer; return $buffer;
} }
$dom->loadHTML($html); $dom->loadHTML($html);
// Traitement des balises <img>
$images = $dom->getElementsByTagName('img'); $images = $dom->getElementsByTagName('img');
foreach ($images as $img) { foreach ( $images as $img ) {
$width = $img->getAttribute('width'); $width = $img->getAttribute('width');
$height = $img->getAttribute('height'); $height = $img->getAttribute('height');
$width = $width ? intval($width) : null; $width = $width ? intval($width) : null;
$height = $height ? intval($height) : null; $height = $height ? intval($height) : null;
if ($img->hasAttribute('src') && preg_match('/^https?:\/\//', $img->getAttribute('src'))) { if ( $img->hasAttribute('src') && preg_match('/^https?:\/\//', $img->getAttribute('src')) ) {
$img->setAttribute('src', wp_thumbor_transform_url($img->getAttribute('src'), $width, $height)); $img->setAttribute('src', wp_thumbor_transform_url($img->getAttribute('src'), $width, $height));
} }
if ($img->hasAttribute('data-src') && preg_match('/^https?:\/\//', $img->getAttribute('data-src'))) { if ( $img->hasAttribute('data-src') && preg_match('/^https?:\/\//', $img->getAttribute('data-src')) ) {
$img->setAttribute('data-src', wp_thumbor_transform_url($img->getAttribute('data-src'), $width, $height)); $img->setAttribute('data-src', wp_thumbor_transform_url($img->getAttribute('data-src'), $width, $height));
} }
if ($img->hasAttribute('srcset')) { if ( $img->hasAttribute('srcset') ) {
$srcsetItems = explode(',', $img->getAttribute('srcset')); $srcsetItems = explode(',', $img->getAttribute('srcset'));
$newSrcset = array(); $newSrcset = array();
foreach ($srcsetItems as $item) { foreach ( $srcsetItems as $item ) {
$parts = preg_split('/\s+/', trim($item)); $parts = preg_split('/\s+/', trim($item));
if (!empty($parts[0]) && preg_match('/^https?:\/\//', $parts[0])) { if ( ! empty($parts[0]) && preg_match('/^https?:\/\//', $parts[0]) ) {
$newURL = wp_thumbor_transform_url($parts[0]); $newURL = wp_thumbor_transform_url($parts[0]);
$newSrcset[] = isset($parts[1]) ? $newURL . ' ' . $parts[1] : $newURL; $newSrcset[] = isset($parts[1]) ? $newURL . ' ' . $parts[1] : $newURL;
} else { } else {
@@ -314,12 +247,12 @@ function wp_thumbor_output_buffer_callback($buffer) {
} }
$img->setAttribute('srcset', implode(', ', $newSrcset)); $img->setAttribute('srcset', implode(', ', $newSrcset));
} }
if ($img->hasAttribute('data-srcset')) { if ( $img->hasAttribute('data-srcset') ) {
$srcsetItems = explode(',', $img->getAttribute('data-srcset')); $srcsetItems = explode(',', $img->getAttribute('data-srcset'));
$newSrcset = array(); $newSrcset = array();
foreach ($srcsetItems as $item) { foreach ( $srcsetItems as $item ) {
$parts = preg_split('/\s+/', trim($item)); $parts = preg_split('/\s+/', trim($item));
if (!empty($parts[0]) && preg_match('/^https?:\/\//', $parts[0])) { if ( ! empty($parts[0]) && preg_match('/^https?:\/\//', $parts[0]) ) {
$newURL = wp_thumbor_transform_url($parts[0]); $newURL = wp_thumbor_transform_url($parts[0]);
$newSrcset[] = isset($parts[1]) ? $newURL . ' ' . $parts[1] : $newURL; $newSrcset[] = isset($parts[1]) ? $newURL . ' ' . $parts[1] : $newURL;
} else { } else {
@@ -329,10 +262,53 @@ function wp_thumbor_output_buffer_callback($buffer) {
$img->setAttribute('data-srcset', implode(', ', $newSrcset)); $img->setAttribute('data-srcset', implode(', ', $newSrcset));
} }
} }
// Renvoie le document HTML complet, préservant <html>, <head> et <body>.
// Traitement des éléments pour les images de background
$allElements = $dom->getElementsByTagName('*');
foreach ( $allElements as $element ) {
// Ne pas traiter les <script>
if ( strtolower($element->tagName) === 'script' ) {
continue;
}
// Traitement du style inline pour les background-images
$style = $element->getAttribute('style');
if ( ! empty($style) ) {
$newStyle = preg_replace_callback('/url\((["\']?)(https?:\/\/[^"\'\)]+)\1\)/i', function($matches) {
$url = $matches[2];
$transformed = wp_thumbor_transform_url($url);
return 'url("' . $transformed . '")';
}, $style);
$element->setAttribute('style', $newStyle);
}
// Traitement des attributs data-image et data-src pour les backgrounds
if ( $element->hasAttribute('data-image') ) {
$dataImage = $element->getAttribute('data-image');
if ( preg_match('/^https?:\/\//', $dataImage) ) {
$element->setAttribute('data-image', wp_thumbor_transform_url($dataImage));
}
}
if ( $element->hasAttribute('data-src') ) {
$dataSrc = $element->getAttribute('data-src');
if ( preg_match('/^https?:\/\//', $dataSrc) ) {
$element->setAttribute('data-src', wp_thumbor_transform_url($dataSrc));
}
}
}
// Renvoyer le document HTML complet, préservant <html>, <head> et <body>
return $dom->saveHTML(); return $dom->saveHTML();
} }
/**
* Démarre la mise en tampon de la sortie pour traiter toutes les images présentes dans le HTML final.
*/
function wp_thumbor_start_output_buffer() {
if ( ! is_admin() && ! defined('DOING_AJAX') ) {
ob_start('wp_thumbor_output_buffer_callback');
}
}
add_action('template_redirect', 'wp_thumbor_start_output_buffer');
/** /**
* Initialisation du plugin (chargement des traductions, etc.). * Initialisation du plugin (chargement des traductions, etc.).
*/ */