sobreescribir_modulo_joomla

Cómo hacer cambios en un módulo de Joomla 3 – actualizado

16 abril, 2015 7 comentarios

Joomla es uno de los CMS más empleados para la gestión de un sitio web. Parte de su potencia radica en la multitud de extensiones que tiene desarrolladas. No hay más que visitar la web oficial para darse cuenta de la variedad de componentes, módulos o plugins disponibles.

Como profesor de cursos de desarrollo web, no me limito a enseñar el manejo de la interfaz de Joomla, sino que creo importante enseñar cómo está estructurado un gestor de contenidos. Y es que un futuro cliente puede tener necesidades que no estén programadas en los módulos disponibles y debemos saber cómo y dónde modificar para poder obtener resultados personalizados.

En el tutorial de hoy hablaré de la personalización del aspecto y funcionalidad de un módulo Joomla. ¿Dispuesto a modificar un módulo en Joomla 3? Te invito a seguir leyendo.

Como habrás leído en multitud de tutoriales, un módulo es una extensión flexible y ligera, normalmente embebida en una capa (position) dispuesta en una plantilla. Algunos de los módulos que Joomla 3 tiene por defecto son:

Cambiar el menú por defecto de Joomla

El ejemplo que nos servirá para guiar la explicación es la modificación del módulo menú (mod_menu).

Al añadir un menú lateral en una plantilla de Joomla, por defecto se crea un menú vertical. Pero… ¿por qué no modificar el módulo para que se muestre un menú dropdown?

Si nuestros ítems de menú tienen cierto nivel de anidamiento, resulta más atractivo un menú desplegable que se muestre u oculte a voluntad del usuario.

Consideraciones previas

Se empleará la versión de Joomla 3.1 y se establecerá la plantilla por defecto beez3. Se asume que se añade un módulo de menú en la posición position-7. Debido a que el menú a mostrar consta de muchos ítems, se sobrescribirá el módulo para construir un menú dropdown en Joomla.

Menú desplegable en Joomla

Menú desplegable en Joomla

Para mostrar una visión general de la estructura y funcionamiento de un módulo de Joomla haremos uso de la siguiente imagen:

Estructura de un módulo

Estructura de un módulo

Joomla está construido siguiendo el patrón MVC por lo que resulta rápido y fácil modificar la vista del módulo. La estructura básica de un módulo (por ejemplo para modules/<mod_kadum>) en Joomla está formada por los ficheros:

<mod_kadum>.xml Posee información sobre el módulo: nombre, versión, autor, descripción y los ficheros o carpetas necesarios para la instalación. También puede contener los parámetros de configuración del módulo una vez instalado (en la etiqueta <config>).

<mod_kadum>.php (Se podría considerar el controlador del módulo) Es el principal punto de entrada al módulo. En él se llevan a cabo las tareas de inicialización de los datos empleados en el módulo (mediante llamadas a métodos de la clase definida en el archivo helper.php) y la carga de la plantilla que muestra esos datos (mediante la inclusión del fichero /tmpl/default.php)

helper.php (Se podría considerar el modelo del módulo) Contiene la clase encargada de llevar a cabo la obtención de los datos que se mostrarán en el módulo.

tmpl/default.php (Se podría considerar la vista del módulo) Es la plantilla del módulo. Coge la información guardada en las variables definidas en <mod_kadum>.php y genera el HTML a mostrar.

Volvamos a la imagen que mostramos anteriormente.

Funcionamiento de un módulo

Funcionamiento de un módulo

Una vez instalado el módulo, gracias al fichero <mod_kadum>.xml se podrán configurar diversos aspectos del módulo que se almacenan en la base de datos (paso 0).

Cuando se accede a la parte pública de Joomla, el fichero <mod_kadum>.php llama a los métodos de la clase definida en fichero helper.php (paso 1). Los métodos obtienen información de la base de datos (paso 2) y la devuelven a <mod_kadum>.php. Esa información se guarda en variables. Por último, se invoca al fichero default.php (paso 3) el cual haciendo uso de las variables definidas en <mod_kadum>.php genera la salida HTML.

Ya que nos interesa modificar el aspecto visual, es decir la vista, el primer razonamiento nos llevaría a pensar que sólo tenemos que modificar el fichero /tmpl/ default.php.

Aunque se obtendría el resultado esperado, no es una buena práctica. No se recomienda modificar directamente ese fichero.

Por lo tanto, ¿qué se debe hacer para modificar el módulo?
Joomla permite llevar a cabo la sobrescritura de dicho fichero, para ello, lo recomendado es copiar el fichero default.php a nuestra plantilla.

¿Dónde se debe copiar el fichero default.php?
Asumiendo que estamos empleando la plantilla por defecto denominada beez3 y que el nombre genérico de nuestro módulo es <mod_kadum>, la ruta es:

/templates/beez3/html/<mod_kadum>/

Observa que sólo se copia el fichero .php dentro de la carpeta <mod_kadum> NO hay que incluirlo dentro de:
/templates/beez3/html/<mod_kadum>/tmpl/

Joomla se encarga de utilizar el fichero default ubicado dentro de templates/beez3/html/<mod_kadum>/ por lo que para sobrescribir nuestros módulos de Joomla, utilizaremos esta técnica.

Tan sólo tiene sentido sobrescribir el fichero default.php. De hecho Joomla sólo atiende a ese fichero, no tiene sentido sobrescribir el modelo (helper.php) o el controlador (<mod_kadum>.php), ya que en ese caso estaríamos creando un nuevo módulo. Tema que se tratará en entradas sucesivas.

Modificación de default.php

Una vez explicada la estructura y funcionamiento de los ficheros que componen un módulo, pasemos a programar el menú dropdown.

Debemos crear un menú (por ejemplo Menu Lateral) con diversos ítems de menú. Además, se tendrá que añadir un módulo de menú (mod_menu) y asociarle el menú creado. Elegimos la posición position-7. No entramos a explicar cómo llevar a cabo tal tarea.

Accediendo a la parte pública de nuestro joomla, el resultado será similar al mostrado en la siguiente imagen:

Modulo mod_menu por defecto

Modulo mod_menu por defecto

Dentro de /templates/beez3/html/ creamos la carpeta mod_menu
Una vez creada la carpeta, copiamos en su interior el fichero /modules/mod_menu/tmpl/default.php

El fichero default.php se encarga de la generación de los menús existentes en nuestro sitio Joomla. El menú horizontal no queremos  modificarlo, sólo queremos modificar el menú lateral añadido a la posición position-7, por lo que lo primero que llevaremos a cabo es una condición.

Si la posición del módulo es position-7
    Construimos nuestro menú dropdown
En caso contrario
    Dejamos la construcción por defecto
Fin si

Ya podemos pasar a modificar el fichero copiado para generar el menú en forma de acordeón.
Buscamos el código (línea 15 aproximadamente):

<ul class="nav menu<?php echo $class_sfx;?>"<?php

Y lo sustituimos por:

<?php
/*
* No se desea modificar el menú superior
*/
if($module->position == 'position-7') : ?>
//Construimos nuestro menu lateral desplegable
<?php else: ?>
<ul class="nav menu <?php echo $class_sfx;?>"<?php

A continuación, se debe cerrar la condición. Al final del archivo (después de ?></ul>) añadimos el código:

<?php endif; ?>

De esta forma, nos aseguramos que en Joomla el menú desplegable en acordeón se construya sólo para la position-7
Modificamos la línea:

//Construimos nuestro menu lateral desplegable

Por el código:

<?php
//Añadir hoja de estilos y fichero javascript
$doc = JFactory::getDocument();
$doc->addStyleSheet(JURI::base(true).'/templates/beez3/html/mod_menu/mi_mod_menu.css');
$doc->addScript(JURI::base(true).'/templates/beez3/html/mod_menu/mi_mod_menu.js', 'text/javascript');
?>

<ul class="menu_lateral"<?php
    $tag = '';
    if ($params->get('tag_id') != null)
    {
        $tag = $params->get('tag_id').'';
        echo ' id="'.$tag.'"';
    }
?>>
<?php
foreach ($list as $i => &$item) :
    $class = 'item-'.$item->id;
    if ($item->id == $active_id)
    {
        $class .= ' current';
    }

    if (in_array($item->id, $path))
    {
        $class .= ' active';
    }
    elseif ($item->type == 'alias')
    {
        $aliasToId = $item->params->get('aliasoptions');
        if (count($path) > 0 && $aliasToId == $path[count($path) - 1])
        {
            $class .= ' active';
        }
        elseif (in_array($aliasToId, $path))
        {
            $class .= ' alias-parent-active';
        }
    }

    if ($item->type == 'separator')
    {
        $class .= ' divider';
    }

    if ($item->deeper)
    {
        $class .= ' deeper';
    }

    if ($item->parent)
    {
        $class .= ' parent';
    }

    if (!empty($class))
    {
        $class = ' class="'.trim($class) .'"';
    }

    echo '<li'.$class.'>';
    
    //Tiene elementos hijos
    if($item->deeper){
        if (in_array($item->id, $path))
            echo '<i class="menos"></i>';
        else
            echo '<i class="mas"></i>';
    }

    // Render the menu item.
    switch ($item->type) :
        case 'separator':
        case 'url':
        case 'component':
        case 'heading':
            require JModuleHelper::getLayoutPath('mod_menu', 'default_'.$item->type);
            break;

        default:
            require JModuleHelper::getLayoutPath('mod_menu', 'default_url');
            break;
    endswitch;

    // The next item is deeper.
    if ($item->deeper)
    {
        echo '<ul class="nav-child">';
    }
    // The next item is shallower.
    elseif ($item->shallower)
    {
        echo '</li>';
        echo str_repeat('</ul></li>', $item->level_diff);
    }
    // The next item is on the same level.
    else {
        echo '</li>';
    }
endforeach;
?></ul>

Analizando el código, podrás observar que es muy parecido al código habitual para la construcción del menú, por lo que nos centraremos en la explicación del código diferente.

if($module->position == 'position-7') : ?>

Si la posición en la que se ha añadido el módulo es position-7.
Este condicional se podrá modificar para contemplar diferentes posiciones a las que añadir nuestro módulo.

<?php
//Añadir hoja de estilos y fichero javascript
$doc = JFactory::getDocument();
$doc->addStyleSheet(JURI::base(true).'/templates/beez3/html/mod_menu/mi_mod_menu.css');
$doc->addScript(JURI::base(true).'/templates/beez3/html/mod_menu/mi_mod_menu.js', 'text/javascript');
?>

Se añade una hoja de estilos y un fichero javascript, cuyo código podrás encontrar posteriormente:

  • /templates/beez3/html/mod_menu/mi_mod_menu.css
  • /templates/beez3/html/mod_menu/mi_mod_menu.js

Con el siguiente código:

//Tiene elementos hijos
if($item->deeper){
    if (in_array($item->id, $path))
        echo '<i class="menos"></i>';
    else
        echo '<i class="mas"></i>';
}

En la primera condición se evalúa si el ítem de menú a renderizar tiene subítems, en cuyo caso le añadiremos el botón para desplegar/contraer el menú.

En la segunda condición (if – else) se observa si el identificador del ítem a renderizar se encuentra en la variable path, en cuyo caso se añade una etiqueta i con la clase menos. En caso contrario, con la clase mas.

De esa forma nos aseguramos que si accedemos a la información de un subítem, cuando se recargue la página, los ítems padres del subítem accedido permanecerán desplegados.

En la siguiente imagen, se asume que hemos accedido a la información de One, por lo que al recargar la página su padre (3 Puertas) debe aparecer desplegado.

Padre desplegado

Padre desplegado

 

Definición de la hoja de estilos

La hoja de estilos para conseguir nuestro menú en forma de acordeón será la mostrada a continuación. No se entrará en detalle en la explicación del código.

.menu_lateral {
    padding:0;
    margin:0;
    list-style-type:none;
    font-size:13px;
}
.menu_lateral li{
    position:relative;
}
.menu_lateral li ul{
    list-style-type:none;
    margin:0;
    padding:0;
    display:none;
}
.menu_lateral .active > ul{
    display:block;
}
.menu_lateral li a, .menu_lateral li span{
    border-bottom:1px solid #444 !important;
    display:block;
    padding:2px 0 2px 12px;
    text-decoration:none;
    color:#444 ;
    background:none;
}
.menu_lateral li ul li a, .menu_lateral li ul li span{
    padding:2px 0 2px 22px;
}
.menu_lateral li ul li ul li a, .menu_lateral li ul li ul li a span{
    padding:2px 0 2px 32px;
}
.menu_lateral a:hover{
    color:#222 ;
    background-color:#CCC;
}
.menu_lateral .current>a{
    color:#111 !important;
    font-weight:bold;
}
.menu_lateral i{
    width: 11px;
    height: 11px;
    display: block;
    cursor: pointer;
    position: absolute;
    right: 7px;
    top: 7px;
}
.menu_lateral .mas{
    background-image:url("./masmenos.png");
    background-repeat:no-repeat;
    background-position:-11px 0;
}
.menu_lateral .menos{
    background-image:url("./masmenos.png");
    background-repeat:no-repeat;
    background-position:0 0;
}

 

Construcción del fichero javascript

El código javascript que se empleará para desplegar o contraer los subítems es el siguiente:

jQuery(document).ready(function() {
    jQuery('.mas').click(function(){
        if(jQuery(this).hasClass('menos')){
            jQuery(this).closest('li').children('ul').fadeOut();
            jQuery(this).addClass('mas').removeClass('menos');
        }else if(jQuery(this).hasClass('mas')){
            jQuery(this).closest('li').children('ul').fadeIn();
            jQuery(this).addClass('menos').removeClass('mas');
        }
    });
    jQuery('.menos').click(function(){
        if(jQuery(this).hasClass('mas')){
            jQuery(this).closest('li').children('ul').fadeIn();
            jQuery(this).addClass('menos').removeClass('mas');
        }else if(jQuery(this).hasClass('menos')){
            jQuery(this).closest('li').children('ul').fadeOut();
            jQuery(this).addClass('mas').removeClass('menos');
        }
    });
});

Se evalúa el evento click sobre las clases CSS mas y menos:

jQuery('.mas').click(function(){...}
jQuery('.menos').click(function(){...}

Al hacer clic se evalúa si el elemento pulsado tiene la clase menos, en cuyo caso:

  • Escondemos sus subitems (fadeOut)
  • Le añadimos la clase mas y le quitamos la clase menos (para modificar su imagen)

O si el elemento, tiene la clase mas, en cuyo caso:

  • Mostramos sus subitems (fadeIn)
  • Le añadimos la clase menos y le quitemos la clase mas (para modificar su imagen)
if(jQuery(this).hasClass('menos')){
    jQuery(this).closest('li').children('ul').fadeOut();
    jQuery(this).addClass('mas').removeClass('menos');
}else if(jQuery(this).hasClass('mas')){
    jQuery(this).closest('li').children('ul').fadeIn();
    jQuery(this).addClass('menos').removeClass('mas');
}

 

Conclusión

Con esto hemos conseguido que el menú añadido en la position-7 se comporte como menú desplegable. El resto de menús añadidos en nuestro sitio Joomla tendrán el comportamiento por defecto.
Podrás descargar la carpeta con los archivos que debes añadir dentro de /templates/<tu-plantilla>/html/ para ello hazte seguidor de nuestro Facebook pulsando sobre la parte izquierda (el icono de la F) del siguiente botón.

En el siguiente tutorial que estamos preparando, se explicará cómo construir un módulo en Joomla 3.

Actualizado: Ya tienes disponible el siguiente tutorial.


Por favor, pulsa el botón Me gusta de facebook para hacerte seguidor de KadumWeb y poder acceder a los archivos descargables

Gracias por hacerte seguidor, pulsa aquí para acceder a los archivos descargables. Descomprime el archivo con la clave: kadummenu




Volver al blog

Comentarios (7)

Rafael Andrés Medina Mendez dijo:

Muchas gracias, por el tiempo y hacer una explicación tan detallada.

    Laura dijo:

    A ti, por leernos

Librado dijo:

Excelente aportación y explicación justa para principiantes como yo. Si la posición 7 tiene un sub-menú que depende del menú principal…que archivo debería recibir la ID?

Librado dijo:

Buen día!Necesito tener categorías en menú principal y sub-menú en modulo lateral izquierdo. Lógicamente cada categoría envía una ID al presionar sobre ella, esta ID debería llegar al modulo de sub-categorías…como hago para recibir esa ID?en que archivo?

    Jose dijo:

    Buenas, Librado:
    En el actual artículo sólo se trata la sobrescritura (override) de la vista de un módulo de Joomla existente. La pregunta que planteas creo que está más relacionada con el tutorial de Cómo crear un módulo en Joomla 3.
    En ese tutorial se explica la estructura y funcionalidad de los diversos ficheros que componen un módulo en Joomla.

    Para poder obtener el identificador de la categoría, debes construir un método en el fichero helper.php con el código:
    $jinput = JFactory::getApplication()->input;
    $id_categoria = $jinput->get('id', '0', 'int');

    En este enlace podrás ver los diferentes tipos de filtros que acepta el método.
    $jinput->get('nombre_variable_recoger', 'valor_por_defecto', 'filtro');

    Además de obtener el identificador, estaría bien comprobar que estás en un ítem de categorías. Para lo que debes recoger el valor de la variable view siguiendo la misma filosofía:
    $vista = $jinput->get('view', '', 'word');

    Es posible que hayas visto que mediante JRequest::getVar(‘id’), también se obtiene el id de la categoría. En versiones antiguas de Joomla se usaba el método estático , pero está en desuso.

    El método definido en helper.php que obtiene el id de la categoría debe ser llamado desde el fichero php que se llama igual que el módulo (actúa como controlador).

    Espero que esto pueda ayudarte a resolver tu duda. Un saludo.

Ivonne dijo:

Hola buenos días.

Últimamente e tenido un problema en el menú. Esta el main menu, una pestaña de busqueda, de usuarios y el carrito de compras, pero al desplegar una y necesita desplegar otra, la anterior no colapsa. No se en que parte pueda estar mal.

La pagina es http://www.mueblesbycsa.com.mx

Un saludo

    Laura dijo:

    Hola, Ivonne:
    Para la construcción del menú superior donde se muestran las 4 opciones:

    1. el main menu
    2. formulario de búsqueda
    3. acceso a usuarios
    4. carrito

    ¿qué módulo de Joomla has utilizado? ¿Es el módulo menú de Joomla o uno de terceros? La plantilla de joomla es la protostar ¿verdad?

    Hemos analizado la funcionalidad del menú superior. Según se aprecia en el código fuente, sí se hace correctamente el cambio de la clase collapse a in collapse al pulsar sobre una misma opción.

    De modo que si, por ejemplo, se pulsa varias veces sobre la lupa, sí se muestra u oculta el formulario de búsqueda.
    Sin embargo, parece que no está contemplado en el código la posibilidad de ocultar una opción al pulsar sobre otra. De modo que, si por ejemplo, se está mostrando el formulario de búsqueda, al pulsar, por ejemplo, sobre el acceso a clientes, se muestran sendos formularios.

Leave a Reply to Laura Cancelar