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

Información
Siempre deberíamos realizar el ejercicio de preguntarnos si realmente es necesario aplicar estos estilos globales. ¿Hay algún componente que resuelva mi necesidad? ¿Puedo realizarlo con clases css utilitarias?

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

Información
En muy escasas ocasiones necesitaremos reutilizar un script entre páginas y deberíamos evitarlo en la medida de lo posible.

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

Antes
Después
  1. Definimos la ruta destino en /sede/portal/casaculturas/
  2. Creamos su template e importamos las bases del arquetipo
  3. 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
    	
    Casa de las culturas
    |-- Presentación
    |-- Información y Atención Social
    |-- Programa Antirrumores
    |-- Reagrupación Familiar
    |-- Esfuerzo de Integración
    |-- Inserción por Arraigo
    |-- Formación por la diversidad cultural
    |-- Retorno Voluntario
    |-- Acogida Municipal
    |-- Clases de Español (Verano)
    |-- Interpretación y Traducción
    |-- Aula Intercultural
    |-- Actividades
    |-- Cesión de Espacios
    |-- Órganos de Participación
    |-- Normativa
    |-- Concursos
    |-- Publicaciones
    `-- Tu Opinión Importa
    
    

    Antes

    	
    Casa de las culturas
    |-- Presentación
    |-- Participación
    |-- Oferta formativa
    `-- Servicios
        |-- Normativa
        |-- Premios y concursos
        |-- Publicaciones
        `-- Trámites
    
    

    Después

  4. Usamos el componente portal/index e integramos sus servicios principales Ver código
  5. 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.

Antes
Después

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:if="${not #strings.isEmpty(button)}">
		<sede:component fragment="/fragmentos/componentes/v2/button" props="${button}"></sede:component>
	</th:block>
</th:block>

<!-- USO -->
<sede:component fragment="../../ejemplo.component.xml" title="Ejemplo" button="{
	text: 'boton ejemplo',
	variant: 'primary'
}"></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}" parent="${id}"></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>