Dans un fichier _public.php du thème :

<?php
# -- BEGIN LICENSE BLOCK ----------------------------------
#
# This file is part of Dotclear 2.
#
# Copyright (c) 2003-2008 Olivier Meunier and contributors
# Licensed under the GPL version 2.0 license.
# See LICENSE file or
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
#
# -- END LICENSE BLOCK ------------------------------------
if (!defined('DC_RC_PATH')) { return; }

# Surcharge EntryFirstImage
$GLOBALS['core']->tpl->addValue('EntryFirstImage',array('tplMytpl','MyEntryFirstImage'));

class tplMytpl
{
/*dtd
<!ELEMENT tpl:EntryFirstImage - O -- Extracts entry first image if exists -->
<!ATTLIST tpl:EntryAuthorEmail
size (sq|t|s|m|o) #IMPLIED -- Image size to extract
class CDATA #IMPLIED -- Class to add on image tag
with_category (1|0) #IMPLIED -- Search in entry category description if present (default 0)
with_post (1|0) #IMPLIED -- Search in entry post content if present (default 1)
src_only (1|0) #IMPLIED -- return src only if present (default 0)
>
*/
public static function MyEntryFirstImage($attr)
{
$size = !empty($attr['size']) ? $attr['size'] : '';
$class = !empty($attr['class']) ? $attr['class'] : '';
$with_category = !empty($attr['with_category']) ? 'true' : 'false';
$with_post = 'true';
if (isset($attr['with_post'])) {
$with_post = !empty($attr['with_post']) ? 'true' : 'false';
}
$src_only = !empty($attr['src_only']) ? 'true' : 'false';

return
"<?php echo tplMytpl::EntryFirstImageHelper('".addslashes($size)."',".
$with_category.",".$with_post.",".$src_only.",'".addslashes($class)."'); ?>";
}

# First post image helpers
public static function EntryFirstImageHelper($size,$with_category=false,$with_post=true,$src_only=false,$class="")
{
if (!preg_match('/^sq|t|s|m|o$/',$size)) {
$size = 's';
}

global $core, $_ctx;

$p_url = $core->blog->settings->public_url;
$p_site = preg_replace('#^(.+?//.+?)/(.*)$#','$1',$core->blog->url);
$p_root = $core->blog->public_path;

$pattern = '(?:'.preg_quote($p_site,'/').')?'.preg_quote($p_url,'/');
$pattern = sprintf('/<img.+?src="%s(.*?\.(?:jpg|gif|png))"/msu',$pattern);

$src = '';

# We first look in post content
if ($with_post && $_ctx->posts )
{
$subject = $_ctx->posts->post_excerpt_xhtml.$_ctx->posts->post_content_xhtml.$_ctx->posts->cat_desc;
if (preg_match_all($pattern,$subject,$m) > 0)
{
foreach ($m[1] as $i) {
if (($src = self::ContentFirstImageLookup($p_root,$i,$size)) !== false) {
# Fixe un bug sous un environement windows qui se melange les '\' et les'/' avec dirname !
$dirname_i = str_replace('\', '/', dirname($i));
$src = $p_url.($dirname_i != '/' ? $dirname_i : '').'/'.$src;
break;
}
}
}
}

# No src, look in category description if available
if (!$src && $with_category && $_ctx->categories)
{
if (preg_match_all($pattern,$_ctx->categories->cat_desc,$m) > 0)
{
foreach ($m[1] as $i) {
if (($src = self::ContentFirstImageLookup($p_root,$i,$size)) !== false) {
$src = $p_url.(dirname($i) != '/' ? dirname($i) : '').'/'.$src;
break;
}
}
};
}

if ($src) {
if ($src_only)
{
echo $src;
}
else
{
echo '<img alt="" src="'.$src.'" class="'.$class.'" />';
}
}
}

private static function ContentFirstImageLookup($root,$img,$size)
{
# Get base name and extension
$info = path::info($img);
$base = $info['base'];

if (preg_match('/^\.(.+)_(sq|t|s|m)$/',$base,$m)) {
$base = $m[1];
}

$res = false;
if ($size != 'o' && file_exists($root.'/'.$info['dirname'].'/.'.$base.'_'.$size.'.jpg'))
{
$res = '.'.$base.'_'.$size.'.jpg';
}
else
{
$f = $root.'/'.$info['dirname'].'/'.$base;
if (file_exists($f.'.'.$info['extension'])) {
$res = $base.'.'.$info['extension'];
} elseif (file_exists($f.'.jpg')) {
$res = $base.'.jpg';
} elseif (file_exists($f.'.png')) {
$res = $base.'.png';
} elseif (file_exists($f.'.gif')) {
$res = $base.'.gif';
}
}

if ($res) {
return $res;
}
return false;
}
}
?>