Skip to content

Commit d2ec007

Browse files
Merge pull request #233 from JLG-WOCFR-DEV/codex/add-image-preview-to-image-list-table
Add thumbnail previews to broken images admin table
2 parents da7cf1d + 3419caf commit d2ec007

File tree

3 files changed

+179
-12
lines changed

3 files changed

+179
-12
lines changed

liens-morts-detector-jlg/assets/css/blc-admin-styles.css

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@
6363
overflow-wrap: anywhere;
6464
}
6565

66+
.wp-list-table td.column-image_details .blc-image-details {
67+
display: flex;
68+
align-items: flex-start;
69+
gap: 12px;
70+
}
71+
72+
.blc-image-preview {
73+
flex: 0 0 auto;
74+
max-width: 96px;
75+
}
76+
77+
.blc-image-preview__img {
78+
display: block;
79+
width: 100%;
80+
height: auto;
81+
max-height: 96px;
82+
border-radius: 4px;
83+
object-fit: cover;
84+
background-color: #f6f7f7;
85+
}
86+
87+
.blc-image-details__content {
88+
flex: 1 1 auto;
89+
min-width: 0;
90+
}
91+
6692
@media (max-width: 782px) {
6793
.wp-list-table {
6894
display: block;
@@ -138,6 +164,20 @@
138164
.wp-list-table td .row-actions span {
139165
display: inline-flex;
140166
}
167+
168+
.wp-list-table td.column-image_details .blc-image-details {
169+
flex-direction: column;
170+
gap: 10px;
171+
}
172+
173+
.blc-image-preview {
174+
width: clamp(96px, 45vw, 140px);
175+
max-width: 100%;
176+
}
177+
178+
.blc-image-preview__img {
179+
max-height: 160px;
180+
}
141181
}
142182

143183
/*

liens-morts-detector-jlg/includes/class-blc-images-list-table.php

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,82 @@ public function get_columns() {
4949
* Gère le rendu de la colonne "Image Cassée".
5050
*/
5151
protected function column_image_details($item) {
52-
// L'URL de l'image est maintenant un lien cliquable
53-
$output = sprintf(
54-
'<strong><a href="%s" target="_blank" rel="noopener noreferrer" title="%s">%s</a></strong>',
55-
esc_url($item['url']),
56-
esc_attr__('Vérifier cette image (nouvel onglet)', 'liens-morts-detector-jlg'),
57-
esc_html($item['url'])
58-
);
59-
$output .= sprintf(
52+
$image_url = isset($item['url']) && is_string($item['url']) ? trim($item['url']) : '';
53+
$anchor_text = isset($item['anchor']) && is_string($item['anchor']) ? trim($item['anchor']) : '';
54+
$alt_text = $anchor_text !== ''
55+
? $anchor_text
56+
: esc_html__("Aperçu de l'image cassée", 'liens-morts-detector-jlg');
57+
58+
$attachment_id = 0;
59+
if (isset($item['attachment_id']) && is_numeric($item['attachment_id'])) {
60+
$attachment_id = (int) $item['attachment_id'];
61+
}
62+
63+
if ($attachment_id === 0 && $image_url !== '' && function_exists('attachment_url_to_postid')) {
64+
$maybe_id = attachment_url_to_postid($image_url);
65+
if (is_numeric($maybe_id)) {
66+
$attachment_id = (int) $maybe_id;
67+
}
68+
}
69+
70+
$thumbnail_html = '';
71+
if ($attachment_id > 0 && function_exists('wp_get_attachment_image')) {
72+
$thumbnail_html = wp_get_attachment_image(
73+
$attachment_id,
74+
'thumbnail',
75+
false,
76+
[
77+
'class' => 'blc-image-preview__img',
78+
'alt' => $alt_text,
79+
'aria-hidden' => 'false',
80+
'loading' => 'lazy',
81+
'decoding' => 'async',
82+
]
83+
);
84+
if (!is_string($thumbnail_html)) {
85+
$thumbnail_html = '';
86+
}
87+
}
88+
89+
if ($thumbnail_html === '' && $image_url !== '') {
90+
$thumbnail_html = sprintf(
91+
'<img src="%s" alt="%s" aria-hidden="false" loading="lazy" decoding="async" class="blc-image-preview__img" />',
92+
esc_url($image_url),
93+
esc_attr($alt_text)
94+
);
95+
}
96+
97+
$preview_markup = '';
98+
if ($thumbnail_html !== '') {
99+
$preview_markup = sprintf('<div class="blc-image-preview">%s</div>', $thumbnail_html);
100+
}
101+
102+
$link_markup = '';
103+
if ($image_url !== '') {
104+
$link_markup = sprintf(
105+
'<strong><a href="%s" target="_blank" rel="noopener noreferrer" title="%s">%s</a></strong>',
106+
esc_url($image_url),
107+
esc_attr__('Vérifier cette image (nouvel onglet)', 'liens-morts-detector-jlg'),
108+
esc_html($image_url)
109+
);
110+
}
111+
112+
$details_markup = $link_markup;
113+
$details_markup .= sprintf(
60114
'<div class="row-actions"><span>%s <em>%s</em></span></div>',
61115
esc_html__('Nom du fichier :', 'liens-morts-detector-jlg'),
62-
esc_html($item['anchor'])
116+
esc_html($anchor_text)
117+
);
118+
119+
if ($preview_markup === '') {
120+
return $details_markup;
121+
}
122+
123+
return sprintf(
124+
'<div class="blc-image-details">%s<div class="blc-image-details__content">%s</div></div>',
125+
$preview_markup,
126+
$details_markup
63127
);
64-
return $output;
65128
}
66129

67130
/**

tests/BlcDashboardImagesPageTest.php

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class BlcDashboardImagesPageTest extends TestCase
3939
*/
4040
public array $wpdbGetResultsQueries = [];
4141

42+
/**
43+
* @var array<int, array<string, mixed>>
44+
*/
45+
public array $wpdbResults = [];
46+
4247
protected function setUp(): void
4348
{
4449
parent::setUp();
@@ -68,7 +73,7 @@ protected function setUp(): void
6873
$this->wpdbPrepareCalls = [];
6974
$this->wpdbGetVarQueries = [];
7075
$this->wpdbGetResultsQueries = [];
71-
76+
$this->wpdbResults = [];
7277
$GLOBALS['wpdb'] = new class($this) {
7378
/** @var string */
7479
public $prefix = 'wp_';
@@ -98,7 +103,7 @@ public function get_results($query, $output = ARRAY_A)
98103
{
99104
$this->test_case->wpdbGetResultsQueries[] = $query;
100105

101-
return [];
106+
return $this->test_case->getWpdbResults();
102107
}
103108
};
104109

@@ -123,8 +128,21 @@ public function get_results($query, $output = ARRAY_A)
123128
Functions\when('number_format_i18n')->alias(static function ($number, $decimals = 0) {
124129
return number_format((float) $number, (int) $decimals);
125130
});
131+
Functions\when('wp_date')->alias(static function ($format, $timestamp = null, $timezone = null) {
132+
$timestamp = $timestamp ?? time();
133+
134+
if ($timezone instanceof \DateTimeZone) {
135+
$date = new \DateTimeImmutable('@' . (int) $timestamp);
136+
$date = $date->setTimezone($timezone);
137+
138+
return $date->format($format);
139+
}
140+
141+
return gmdate($format, (int) $timestamp);
142+
});
126143

127144
require_once __DIR__ . '/../liens-morts-detector-jlg/includes/blc-admin-pages.php';
145+
require_once __DIR__ . '/../liens-morts-detector-jlg/includes/class-blc-images-list-table.php';
128146
}
129147

130148
protected function tearDown(): void
@@ -208,6 +226,44 @@ public function test_manual_image_check_shows_error_when_manual_trigger_fails():
208226
$this->assertStringContainsString("Le déclenchement immédiat du cron a échoué", $output);
209227
}
210228

229+
public function test_dashboard_images_page_displays_thumbnail_preview_when_available(): void
230+
{
231+
Functions\when('attachment_url_to_postid')->alias(static function ($url) {
232+
return 321;
233+
});
234+
Functions\when('wp_get_attachment_image')->alias(static function ($attachment_id, $size = 'thumbnail', $icon = false, $attr = array()) {
235+
$attributes = '';
236+
237+
if (is_array($attr)) {
238+
foreach ($attr as $name => $value) {
239+
$name = is_string($name) ? $name : (string) $name;
240+
$value = is_scalar($value) ? (string) $value : '';
241+
$attributes .= sprintf(' %s="%s"', esc_attr($name), esc_attr($value));
242+
}
243+
}
244+
245+
return sprintf('<img src="https://example.com/uploads/thumb-%d.jpg"%s />', (int) $attachment_id, $attributes);
246+
});
247+
$list_table = new \BLC_Images_List_Table();
248+
$reflection = new \ReflectionMethod(\BLC_Images_List_Table::class, 'column_image_details');
249+
$reflection->setAccessible(true);
250+
251+
$item = [
252+
'url' => 'https://example.com/uploads/broken-image.jpg',
253+
'anchor' => 'broken-image.jpg',
254+
'post_id' => 42,
255+
'post_title' => 'Sample Post',
256+
'http_status' => 404,
257+
'last_checked_at' => '2024-01-10 12:34:56',
258+
];
259+
260+
$output = (string) $reflection->invoke($list_table, $item);
261+
262+
$this->assertStringContainsString('class="blc-image-preview"', $output);
263+
$this->assertStringContainsString('<img src="https://example.com/uploads/thumb-321.jpg"', $output);
264+
$this->assertStringContainsString('aria-hidden="false"', $output);
265+
}
266+
211267
public function test_dashboard_images_page_handles_empty_dataset_row_types(): void
212268
{
213269
$registered_filters = [];
@@ -275,6 +331,14 @@ public function test_dashboard_images_page_handles_empty_dataset_row_types(): vo
275331
$this->assertStringContainsString('Jamais', $output);
276332
}
277333

334+
/**
335+
* @return array<int, array<string, mixed>>
336+
*/
337+
public function getWpdbResults(): array
338+
{
339+
return $this->wpdbResults;
340+
}
341+
278342
/**
279343
* @param string $name
280344
* @param mixed $default

0 commit comments

Comments
 (0)