Adiós Gravatar: Cómo integrar SP Author Archive en CommentBox y crear una Ficha de Autor PRO en Joomla
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:
- Obligar a CommentBox a usar las fotos de SP Author Archive.
- Eliminar el enlace a Gravatar en los comentarios.
- Crear una Ficha de Autor PRO al final de tus artículos con foto, cargo, edad, ciudad y redes sociales.
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.
- Detalles
- Escrito por: Paco Guio
- Categoría: Joomla
- Tiempo de lectura: 11 mins








