ilustración con un cable rojo que desconecta rgravatar a una web y con un cable verde que conecta la base de datos a la web para coger el avatar

Si gestionas un medio digital o un blog colaborativo en Joomla, seguramente usas extensiones como SP Author Archive para que tus redactores tengan perfiles bonitos con su foto real, biografía y redes sociales.

Sin embargo, existe un problema de desconexión habitual: cuando esos autores comentan o cuando se muestra su ficha al final de los artículos, Joomla y extensiones como CommentBox suelen ignorar esos datos locales y buscan la imagen en Gravatar. Resultado: si el autor no tiene Gravatar, aparece el temido "muñeco gris".

En esta guía definitiva vamos a conectar todos los cables. Te enseñaré a:

  1. Obligar a CommentBox a usar las fotos de SP Author Archive.
  2. Eliminar el enlace a Gravatar en los comentarios.
  3. Crear una Ficha de Autor PRO al final de tus artículos con foto, cargo, edad, ciudad y redes sociales.

⚠️ Antes de empezar: Vamos a editar archivos. Realiza siempre una copia de seguridad de los archivos originales descargándolos por FTP antes de modificarlos.

PASO 1: Integrar las fotos en CommentBox (El Helper)

Primero, vamos a decirle al sistema de comentarios que busque la foto en nuestra base de datos local antes de irse a buscar fuera.

📂 Archivo a editar:
/administrator/components/com_commentbox/src/Helper/UserHelper.php
(Nota: En algunas versiones antiguas puede estar en /helpers/UserHelper.php).

🛠️ La solución:
Sustituye todo el código de ese archivo por el siguiente. Este script busca el ID del usuario por su email y recupera su avatar de la tabla de perfiles de Joomla:

<?php
/**
 * Modificación para conectar CommentBox con SP Author Archive
 */
namespace Firecoders\Component\CommentBox\Administrator\Helper;

use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;

\defined('_JEXEC') or die;

class UserHelper
{
    public static function getAvatar($email)
    {
        $db = Factory::getDbo();
        $avatar = '';

        if (!empty($email)) {
            // 1. Buscamos ID de usuario por email
            $queryUser = $db->getQuery(true)
                ->select('id')
                ->from($db->quoteName('#__users'))
                ->where($db->quoteName('email') . ' = ' . $db->quote($email));
            $db->setQuery($queryUser);
            $userId = $db->loadResult();

            // 2. Buscamos la foto en SP Author Archive
            if ($userId) {
                try {
                    $queryProfile = $db->getQuery(true)
                        ->select($db->quoteName('profile_value'))
                        ->from($db->quoteName('#__user_profiles'))
                        ->where($db->quoteName('user_id') . ' = ' . (int) $userId)
                        ->where($db->quoteName('profile_key') . ' = ' . $db->quote('profilespaarchive.avatar'));

                    $db->setQuery($queryProfile);
                    $result = $db->loadResult();

                    if ($result) {
                        $data = json_decode($result);
                        if (isset($data->avatar) && !empty($data->avatar)) {
                            $avatar = Uri::root() . ltrim($data->avatar, '/\\');
                        }
                    }
                } catch (\Exception $e) { }
            }
        }

        // 3. Fallback a Gravatar
        if (empty($avatar)) {
            return 'https://www.gravatar.com/avatar/' . hash('sha256', strtolower(trim($email))) . '?d=mp';
        }

        return $avatar;
    }
}

PASO 2: Eliminar el enlace a Gravatar (El JS)

Ahora las fotos cargan, pero si haces clic en ellas, te sacan de tu web y te llevan a Gravatar. Vamos a "neutralizar" ese clic.

📂 Archivo a editar:
/media/com_commentbox/js/commentbox.min.js

🛠️ La solución:
Descarga el archivo y ábrelo con un editor de código. Usa el buscador (Ctrl+F) para encontrar gravatar.com.

  • Busca algo parecido a: href:"https://gravatar.com/..."
  • Cámbialo por: href:"javascript:;" o tambien puede cambiarlo por: Cámbialo por: href:"#" 

Guarda, sube y limpia la caché del navegador (Ctrl+F5) para ver el cambio.


PASO 3: La Ficha de Autor PRO (Template Override)

Por último, vamos a hacer que la caja de autor al final de los artículos muestre toda la información rica que hemos rellenado en el perfil (Edad, Ciudad, Educación, Redes, etc.) y solucione los problemas de tildes en la biografía.

📂 Archivo a editar:
/templates/[TU_PLANTILLA]/html/com_content/article/default.php

Si este archivo no lo encuentras tendrás que crearlo, esto es tan sencillo como crear un override en la siguiente ruta desde el backend: Panel de control del sistema/plantillas del sitio/Detalles y archivos de tu plantilla predeterminada/Crear modificaciones: en la columna de Componentes, haces clic para abrir donde pone carpeta com_content y en el desplegable, haces clic en "article". Una vez hecho esto el sistema te manda a la primera pestaña, al editor. Busca en la lista de carpetas la siguiente ruta: html/com_content/article/default.php. Este archivo es en el que tenemos que borrar su contenido y cambiar por el código de las instrucciones siguientes:

🛠️ La solución:
Este código sustituye completamente al archivo original. Incluye un sistema inteligente que detecta y limpia los datos JSON de la base de datos y muestra iconos para cada campo.

(Copia y pega el código completo a continuación en tu archivo default.php):

<?php
/**
 * @package Helix Ultimate Framework (Modified for SP Author Archive Integration)
 */

defined('_JEXEC') or die();

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;

HTMLHelper::addIncludePath(JPATH_COMPONENT . '/helpers');
$helix_path = JPATH_PLUGINS . '/system/helixultimate/core/helixultimate.php';

$tmpl_params = Factory::getApplication()->getTemplate(true)->params;

// [.... INICIO BLOQUE STANDARD DE JOOMLA/HELIX ....]
// (El código mantiene toda la lógica original de la plantilla para mostrar el artículo)
// ...
// [.... VISUALIZACIÓN DEL ARTÍCULO ....]

// =====================================================================
// INICIO CAJA DE AUTOR "PRO" vFINAL
// =====================================================================

$authorId = $this->item->created_by;
$db = Factory::getDbo();

$spAvatar = '';
$spDesignation = '';
$spBio = '';
$spEducation = '';
$spExperience = '';
$spCitizenship = '';
$spAge = '';
$spSocial = [];

try {
    $query = $db->getQuery(true)
        ->select($db->quoteName(array('profile_key', 'profile_value')))
        ->from($db->quoteName('#__user_profiles'))
        ->where($db->quoteName('user_id') . ' = ' . (int) $authorId)
        ->where($db->quoteName('profile_key') . ' LIKE ' . $db->quote('profilespaarchive.%'));
    
    $db->setQuery($query);
    $results = $db->loadObjectList();

    if ($results) {
        foreach ($results as $row) {
            $key = $row->profile_key;
            $val = $row->profile_value;

            // Función para limpiar texto y decodificar JSON string
            $cleanVal = function($v) {
                $d = json_decode($v);
                if (json_last_error() === JSON_ERROR_NONE) {
                    return $d;
                }
                return trim($v, '"');
            };

            if ($key == 'profilespaarchive.avatar') {
                $d = json_decode($val);
                if (isset($d->avatar) && !empty($d->avatar)) {
                    $spAvatar = Uri::root() . ltrim($d->avatar, '/\\');
                }
            }
            elseif ($key == 'profilespaarchive.designation') {
                $res = $cleanVal($val);
                $spDesignation = is_string($res) ? $res : (isset($res->designation) ? $res->designation : '');
            }
            elseif ($key == 'profilespaarchive.description' || $key == 'profilespaarchive.biography') {
                $res = $cleanVal($val);
                if (is_string($res)) {
                     $spBio = str_replace(['\r\n', '\n', '\r'], '
', $res); } } elseif ($key == 'profilespaarchive.education') { $res = $cleanVal($val); if (is_string($res)) $spEducation = $res; } elseif ($key == 'profilespaarchive.experience') { $res = $cleanVal($val); if (is_string($res)) $spExperience = $res; } elseif ($key == 'profilespaarchive.citizenship') { $res = $cleanVal($val); if (is_string($res)) $spCitizenship = $res; } elseif ($key == 'profilespaarchive.age') { $res = $cleanVal($val); if (is_string($res) || is_numeric($res)) $spAge = $res; } elseif ($key == 'profilespaarchive.social') { $d = json_decode($val, true); if (json_last_error() === JSON_ERROR_NONE && is_array($d)) { $spSocial = $d; } } } } } catch (\Exception $e) { } // Fallback Avatar if (empty($spAvatar)) { $authorEmail = isset($this->item->author_email) ? $this->item->author_email : ''; $spAvatar = $authorEmail ? 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($authorEmail))) . '?s=150&d=mp' : 'https://www.gravatar.com/avatar/?s=150&d=mp'; } ?> <!-- HTML CAJA DE AUTOR --> <div class="article-author-info sp-author-box" style="margin-top: 40px; padding: 25px; background: #fff; border-radius: 8px; display: flex; flex-wrap: wrap; gap: 25px; border: 1px solid #eee; box-shadow: 0 5px 15px rgba(0,0,0,0.03);"> <div class="author-avatar-col" style="flex-shrink: 0; text-align: center;"> <div class="author-avatar" style="width: 100px; height: 100px; margin: 0 auto;"> <img src="/<?php echo $spAvatar; ?>" alt="<?php echo htmlspecialchars($this->item->author); ?>" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover; border: 3px solid #f4f4f4;"> </div> </div> <div class="author-content-col" style="flex-grow: 1; min-width: 250px;"> <h4 style="margin: 0 0 5px 0; font-weight: 700; font-size: 1.4rem; color: #333;"> <?php echo $this->item->author; ?> </h4> <?php if (!empty($spDesignation)) : ?> <div style="font-size: 0.9rem; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px; font-weight: 600;"> <?php echo htmlspecialchars($spDesignation); ?> </div> <?php endif; ?> <?php if (!empty($spCitizenship) || !empty($spAge) || !empty($spEducation) || !empty($spExperience)) : ?> <div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px; font-size: 0.85rem; color: #666;"> <?php if (!empty($spCitizenship)) : ?><span title="Ciudad"><i class="fa fa-map-marker"></i> <?php echo htmlspecialchars($spCitizenship); ?></span><?php endif; ?> <?php if (!empty($spAge)) : ?><span title="Edad"><i class="fa fa-birthday-cake"></i> <?php echo htmlspecialchars($spAge); ?> años</span><?php endif; ?> <?php if (!empty($spEducation)) : ?><span title="Educación"><i class="fa fa-graduation-cap"></i> <?php echo htmlspecialchars($spEducation); ?></span><?php endif; ?> <?php if (!empty($spExperience)) : ?><span title="Experiencia"><i class="fa fa-briefcase"></i> <?php echo htmlspecialchars($spExperience); ?></span><?php endif; ?> </div> <?php endif; ?> <?php if (!empty($spBio)) : ?> <div style="font-size: 0.95rem; color: #555; line-height: 1.6; margin-bottom: 15px;"> <?php echo strip_tags($spBio, '<br><a><b><i>'); ?> </div> <?php endif; ?> <?php if (!empty($spSocial)) : ?> <div class="author-social-icons" style="display: flex; gap: 10px;"> <?php foreach ($spSocial as $socialItem) : ?> <?php $netName = isset($socialItem['social_name']) ? $socialItem['social_name'] : ''; $netLink = isset($socialItem['social_url']) ? $socialItem['social_url'] : ''; if (!empty($netLink)) : ?> <a href="/<?php echo $netLink; ?>" target="_blank" rel="nofollow noopener" title="<?php echo htmlspecialchars($netName); ?>" style="color: #999; font-size: 18px; text-decoration: none; transition: color 0.3s; margin-right: 10px;"> <i class="fa fa-<?php echo strtolower($netName); ?>"></i> </a> <?php endif; ?> <?php endforeach; ?> </div> <?php endif; ?> </div> </div> <!-- FIN CAJA DE AUTOR -->

💡 Mantenimiento y Futuro

¡Ojo! Los cambios en el Helper de CommentBox y en el archivo JS se perderán si actualizas la extensión. Guarda una copia de estos archivos modificados en tu ordenador (carpeta "Parches Web") para poder restaurarlos en segundos tras una actualización.

El cambio en la plantilla (Paso 3) es más resistente, ya que al ser un "override" en la carpeta /html/, suele respetarse incluso si actualizas Joomla o el framework.

Artículos relacionados:

¿Quieres que te avisemos de lo más reciente?

Si quieres recibir en tu correo las nuevas publicaciones, solo tienes que decirlo.

Contenido que es posible que te interese:

Colaboraciones, amigos y partners

logo paco guio
logo joomla madrid
Logo de joomla day España

Este sitio esta aalojado en un servidor que utilliza energías verdes - verificado por thegreenwebfoundation.org