Guías de implementación
Un portal desde cero
En términos generales, el desarrollo de un portal va a estar ligado a un área del ayuntamiento.
Debemos comunicarnos con el área en cuestión para llevar a cabo la disposición de la información del portal y sus servicios.
El resultado de esta sección se ve reflejado en este portal de ejemplo
Inicio
Como se menciona en la sección de interfaces la estructura básica de un portal viene dada por un template y una página indice
Nombre portal/servicio
|-- index.xml
`-- _template.xml
Estructura base
Primero debemos definir a que área afecta este portal y distinguir su naturaleza. Si es un portal de nivel superior o un portal de nivel inferior .
En general el portal superior colgará directamente de /sede/portal/. Este agrupará servicios transversales, servicios propios e información estática (presentacion, etc..).
Un portal inferior, a diferencia del anterior, puede estar incluido en un portal mayor y la información que brindará será mas especializada. Muchas veces lo usaremos como resumen de puntos importantes de un tema complejo como una normativa o un nexo entre portales y servicios relacionados en algún aspecto.(Ej: portal de Infancia )
Template
Siempre comenzaremos definiendo el archivo _template del portal definiendo la estructura base obligatoria heredando el arquetipo
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:sede="http://www.zaragoza.es" xmlns:th="http://www.thymeleaf.org" estilo="ayto">
<th:block th:fragment="cssjs">
<th:block th:include="/portal/_template :: cssjs" />
</th:block>
<body>
<th:block th:fragment="header">
<th:block th:include="/portal/_template :: header" />
</th:block>
<th:block th:fragment="menu">
<th:block th:include="/portal/_template :: menu" />
</th:block>
<th:block th:fragment="footer">
<th:block th:include="/portal/_template :: footer" />
</th:block>
</body>
</html>
_template.xml de un portal
Portada
Para la portada debemos usar el
componente "template header"
. Este se encargará mostrar una imagen de portada y disponer el <h1> del portal. Deberemos definir una url base que apuntará a la pagina índice del portal
En los portales internos la cabecera se ocultará pero dejará visible para lectores de pantalla el nombre del portal
...
<th:block th:fragment="menu">
<th:block th:include="/portal/_template :: menu" />
<sede:component fragment="/fragmentos/componentes/v2/template-header"
title="Portal de ejemplo"
baseuri="/sede/portal/sistema-diseno/portal-ejemplo/">
</sede:component>
</th:block>
...
_template.xml de portal-ejemplo
Menú
En los portales superiores, en la mayoría de los casos usaremos el componente megamenú
En el menú deberemos definir una
estructura de navegación coherente, consistente y simple
. Para hacerla consistente entre todas las páginas del portal utilizaremos las propiedades del template aprovechando el fragmento th:fragment="menu"
...
<th:block th:fragment="menu">
<th:block th:include="/portal/_template :: menu" />
<sede:component fragment="/fragmentos/componentes/v2/template-header"
title="Portal de ejemplo"
baseuri="/sede/portal/sistema-diseno/portal-ejemplo/">
</sede:component>
<sede:component fragment="/fragmentos/componentes/v2/megamenu" json="{
'data': [
{
'name': 'Presentación',
'link': '/sede/portal/sistema-diseno/portal-ejemplo/'
},
{
'name': 'Trámites',
'link': '/sede/portal/sistema-diseno/portal-ejemplo/servicio/tramite/'
}
]
}">
</sede:component>
</th:block>
...
_template.xml de portal-ejemplo
Miga de pan
En esta sección, por último, tendremos que agregar la miga de pan con el componente <sede:miga-pan></sede:miga-pan>. Su configuración base es suficiente en esta instancia.
...
<th:block th:fragment="menu">
<th:block th:include="/portal/_template :: menu" />
<sede:component fragment="/fragmentos/componentes/v2/template-header"
title="Portal de ejemplo"
baseuri="/sede/portal/sistema-diseno/portal-ejemplo/">
</sede:component>
<sede:component fragment="/fragmentos/componentes/v2/megamenu" json="{
'data': [
{
'name': 'Presentación',
'link': '/sede/portal/sistema-diseno/portal-ejemplo/'
},
{
'name': 'Trámites',
'link': '/sede/portal/sistema-diseno/portal-ejemplo/servicio/tramite/'
}
]
}">
</sede:component>
<sede:miga-pan></sede:miga-pan>
</th:block>
...
_template.xml de portal-ejemplo
Estilos globales
Si nos vemos en la necesidad de incorporar un estilado que se repite en varias páginas del portal debemos incoporarlo en <th:fragment="cssjs">
...
<th:block th:fragment="cssjs">
<th:block th:include="/portal/_template :: cssjs" />
<style>
/*Estilos globales*/
</style>
</th:block>
...
_template.xml de portal-ejemplo
Scripts globales
Si nos vemos en la necesidad de incorporar un script que se repite en varias páginas del portal debemos incoporarlo en <th:fragment="footer">
...
<th:block th:fragment="footer">
<th:block th:include="/portal/_template :: footer" />
<script>
/*<![CDATA[*/
...
/*]]>*/
</script>
</th:block>
...
_template.xml de portal-ejemplo
index
En su estructura base un portal se compone de la siguiente manera
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sede="http://www.zaragoza.es" lang="es">
<head>
<sede:meta
title="Portal ejemplo"
description="..."
keywords="..."
last-modified="...">
</sede:meta>
</head>
<body>
<sede:content>
<!--/* Contenido */-->
</sede:content>
</body>
</html>
index.xml de portal-ejemplo
Para facilitar el desarrollo de las páginas índices tenemos el componente especial PORTAL/INDEX.
<sede:component fragment="/fragmentos/componentes/portal/index"
title="..."
description="..."
keywords="..."
lastmodified="..."
secciones="{
'1': 'avisos',
'2': 'noticias',
'3': 'calendario',
}"
portalconfig="{
'avisos': {...},
'noticias': {...},
'calendario': {...}
}
}"></sede:component>
...
index.xml de portal-ejemplo
Este componente es un controlador de componentes que permite estructurar rápidamente el portal en base a una configuración
Los elementos / secciones que se pueden utilizar son los definidos en secciones estandarizadas .
Las configuraciones disponibles para este componente está en su respectiva sección de esta documentación (pendiente)
Secciones no estandarizas
En caso de solicitarse un sección no preestablecida como un texto introductorio, un mapa o elementos muy particulares del diseño que se definió para ese portal, podemos hacer uso de la palabra clave "include"
<sede:component fragment="/fragmentos/componentes/portal/index"
title="..."
description="..."
keywords="..."
lastmodified="..."
secciones="{
'0': 'include_introduccion',
'1': 'avisos',
'2': 'noticias',
'3': 'calendario',
}"
portalconfig="{
'avisos': {...},
'noticias': {...},
'calendario': {...},
'include_introduccion': {
include: ':: introduccion',
class: 'py-2 my-05'
},
}
}"></sede:component>
<th:block th:if="false">
<th:block th:fragment="introduccion">
<div class="row">
<div class="col-xs-12 col-sm-8 mb-1">
<p>
Lorem ipsum ...
</p>
</div>
<div class="col-xs-12 col-sm-4 mb-1">
<sede:component fragment="/fragmentos/componentes/v2/image" src="/cont/paginas/noticias/2024/novelas.jpg" ratio="16/9">
</sede:component>
</div>
</div>
</th:block>
</th:block>
...
index.xml de portal-ejemplo
Animaciones
El manejo de las animaciones
viene preconfigurado en este componente. Solamente hay que asignarle el parámetro
needs="aos"
en la configuración del componente.
Este obtendrá el script AOS que se encargará de ejecutar las animaciones preestablecidas
Otras páginas
El resto de páginas las defiremos por la arquitectura de la información establecida con el área del ayuntamiento encargada del portal.
Debido a que definimos una URL base en el componente de la portada en el resto de páginas deberemos definir el H1 para cada una y controlar de mantener una estructura de encabezados coherente
Migrando un portal
La migración de un portal va a depender de su estructura y actualidad
desde CIUDAD
Si el portal es muy antiguo, dependerá de que tan actualizada esté la información. Probablemente se modifique la estructura del menú y el propio contenido
Por seguir un ejemplo práctico tomaremos el portal de casa de las culturas
- Definimos la ruta destino en /sede/portal/casaculturas/
- Creamos su template e importamos las bases del arquetipo
-
Incoporamos megamenú, cabecera y redefinimos su estructura.
- Lo simplificamos eliminando los enlaces a páginas estáticas y los reempleazamos por contenido dinámico
- Eliminamos los múltiples enlaces a distintos tramites e incoporamos el servicio
Antes
Después
-
Usamos el componente
portal/indexe integramos sus servicios principales Ver código - Ajustamos los encabezados en el resto de páginas
desde SEDE
A diferencia de los portales de /ciudad, estos tienen (o deberían) tener información actualizada y la estructura de navegación no debería variar mucho.
Si el portal es sencillo, importando el arquetipo en el template hace la mayoría del trabajo.
En este caso aprovechamos la gráfica que poseía el portal en el componente template-heder
Mantuvimos la navegación y adaptamos un poco los estilos de las secciones de la página. Por otra parte, aprovechamos a agregar los servicios trámites y noticias.
Para el resto de páginas controlamos los niveles de encabezados y adaptamos el contenido adaptando y corregiendo segun sea necesario. (En este caso se aprovechó a corregir imágenes sin texto alternativo)
¿Cómo modificar la portada de la sede?
Ir a : /cont/vistas/portal/sede/sede_v2/imagenes-portada.js
export const images = {
0: {
src: '/cont/vistas/portal/sede/sede_v2/assets/backgrounds/mapa-sombras.png',
href: '/sede/portal/idezar/',
text: 'Descubre el Geoportal de Zaragoza: IDEZar'
},
//...
};
imagenes-portada.js
¿Cómo se genera un componente?
Al maquetar páginas nos encontraremos con estructuras específicas que se repiten a lo largo de un portal.
Estas estructuras son parecidas en su "forma" solamente variando en contenido
En estos casos, para evitar generar estructuras repetidas en varias páginas, lo que podemos hacer es generar un componente
Primero debemos implementar la práctica de preguntarnos si este componente es de utilidad genérica o de forma mas específica es una estructura muy particular que realmente no usaremos fuera del portal que estamos desarrollando.
Hay una serie de componentes útiles para usar como base, por lo que tampoco debemos errar en generar un componente ya creado. El principio fundamental de los componente es la reutilización.
Para generar un componente debemos crear un archivo según el análisis previo. Si es un componente genérico debemos crearlo en /fragmentos/componentes/v2. Si es un componente específico deberemos crearlo en la misma carpeta del portal que estamos desarrollando.
En este último caso, se recomienda utilizar el sufijo x.component.xml para evitar confundirlo con páginas del propio portal
Un componente en su estructura mas básica es simplemente una plantilla .xml que utilizaremos con la tag especial
sede:component
<!--/* /portal/ejemplo.component.xml */-->
<h2 th:text="${title}"></h2>
<!--/* /portal/pagina.xml */-->
<sede:component fragment="../../ejemplo.component" title="Ejemplo"></sede:component>
<!--/* resultado */-->
<h2>Ejemplo</h2>
Para evitar que las propiedades de otros componentes afecten sobre este, debemos colocar un "guard". Este guard detecta si la propiedad se encuentra definida en la tag que invoca el componente, si no está definido asigna un valor por defecto.
De esta forma logramos un sistema de "encapsulamiento" que evita conflictos entre componentes.
<th:block th:fragment="espacios" th:with="
title=${#maps.containsKey(attrs, 'title')}? ${attrs.title} : ${props != null and props.has('title')? props.get('title'): 'título'},
">
<h2 th:text="${title}"></h2>
</th:block>
Dentro del componente podremos comenzar a estructurar el etiquetado correspondiente y utilizar otros componentes. Para tener mayor control sobre la estructura deberemos usar estructuras de control. Generando comprobaciones que aseguren que el componente no reciba elementos no deseados en sus atributos.
Ejemplo: un título vácio.
<th:block th:fragment="espacios" th:with="
title=${#maps.containsKey(attrs, 'title')}? ${attrs.title} : ${props != null and props.has('title')? props.get('title'): 'título'},
">
<h2 th:unless="${#strings.isEmpty(title)}" th:text="${title}"></h2>
<p th:unless="${#strings.isEmpty(content)}"th:utext="${content}"></p>
</th:block>
Podemos generar componentes compuestos, (componentes que utilizan otros componentes). Para ello utilizaremos el atributo especial "props"
<th:block th:fragment="espacios" th:with="
title=${#maps.containsKey(attrs, 'title')}? ${attrs.title} : ${props != null and props.has('title')? props.get('title'): 'título'},
">
<h2 th:unless="${#strings.isEmpty(title)}" th:text="${title}"></h2>
<th:block ">
</th:block>
</th:block>
<!-- USO -->
<sede:component fragment="../../ejemplo.component.xml" title="Ejemplo" ></sede:component>
Cuando querramos implementar estructuras repetidas en un componente. Deberemos usar el atributo json. Este atributo acepta este formato para asignar un conjunto de valores en el que podremos iterar.
Ejemplo: Componente acordión
<th:block th:fragment="accordion" th:with="
id=${#maps.containsKey(attrs, 'id')}? ${attrs.id} : ${'accordion'+#strings.randomAlphanumeric(10)},
">
<th:block th:unless="${#strings.isEmpty(json)}">
<div class="panel-group" th:id="${id}">
<th:block th:each="collapseProps, i : ${json.getJSONArray('data').getArray()}">
<th:block th:unless="${collapseProps.has('hide')}">
<sede:component fragment="/fragmentos/componentes/v2/collapse" props="${collapseProps}" ></sede:component>
</th:block>
</th:block>
</div>
</th:block>
</th:block>
<!-- -->
<sede:component
fragment="/fragmentos/componentes/v2/accordion"
json="{
'data': [
{
'title': 'test 1',
'include': '...',
},
{
'title': 'test 2',
'include': '...',
}
]
}"
>
</sede:component>