Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jpbarbatic/webapp/llms.txt

Use this file to discover all available pages before exploring further.

El módulo de Usuarios gestiona las cuentas del panel de administración. Ofrece un listado paginado de todos los usuarios registrados, un flujo de alta y edición con formulario compartido, cambio de contraseña mediante hash bcrypt con password_hash, subida y redimensionado de foto de perfil usando las funciones GD de PHP, y borrado con confirmación modal. El acceso al listado está protegido por el permiso usuarios.consulta y las operaciones de escritura por usuarios.actualizar.

Modelo de datos

La tabla usuarios almacena las credenciales y los datos personales de cada cuenta. La contraseña se guarda siempre como hash bcrypt (60 caracteres). La columna id_rol referencia la tabla roles con ON DELETE SET NULL, de modo que al borrar un rol los usuarios afectados quedan sin rol asignado.
ColumnaTipoNotas
idint AUTO_INCREMENTClave primaria
nombrechar(20)Requerido
apellidoschar(30)Requerido
emailchar(50)Requerido
telefonochar(12)Opcional
passwordchar(60)Hash bcrypt generado con password_hash
id_rolintFK → roles.id · ON DELETE SET NULL
CREATE TABLE `usuarios` (
  `id`        int(11)   NOT NULL AUTO_INCREMENT,
  `nombre`    char(20)  NOT NULL,
  `apellidos` char(30)  NOT NULL,
  `email`     char(50)  NOT NULL,
  `telefono`  char(12)  DEFAULT NULL,
  `password`  char(60)  NOT NULL DEFAULT '',
  `id_rol`    int(11)   DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `id_rol` (`id_rol`),
  CONSTRAINT `usuarios_ibfk_2`
    FOREIGN KEY (`id_rol`) REFERENCES `roles` (`id`)
    ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci;

Rutas

URLMétodoArchivoDescripción
usuarios/GETpublic/usuarios/index.phpListado paginado de usuarios
usuarios/nuevo.phpGETpublic/usuarios/nuevo.phpFormulario de alta de un nuevo usuario
usuarios/crear.phpPOSTpublic/usuarios/crear.phpInserta el nuevo usuario y redirige a editar
usuarios/guardar.phpPOSTpublic/usuarios/guardar.phpActualiza los datos del usuario existente
usuarios/editar.php?id=NGETpublic/usuarios/editar.phpCarga el formulario con los datos del usuario
usuarios/borrar.php?id=NGETpublic/usuarios/borrar.phpElimina el usuario y redirige al listado
usuarios/guardar_password.phpPOSTpublic/usuarios/guardar_password.phpActualiza la contraseña con hash bcrypt
usuarios/guardar_foto.phpPOSTpublic/usuarios/guardar_foto.phpSube, redimensiona y guarda la foto de perfil

Listado

public/usuarios/index.php declara el permiso requerido antes de cargar el backend, genera el token CSRF de sesión, calcula la paginación y consulta todos los usuarios:
<?php
$permiso = 'usuarios.consulta';

$db = require_once('../../includes/backend.php');

$_SESSION['token'] = uniqid();

$p            = isset($_GET['p']) ? $_GET['p'] : 1;
$items_pagina = 10;
$offset       = ($p - 1) * $items_pagina;

$res = db_select($db, 'SELECT * FROM usuarios', [], $items_pagina, $offset);
extract($res);

$titulo = 'Usuarios';
$vista  = 'usuarios/listado';
require('../../html/plantilla.html.php');
El token generado con uniqid() se almacena en $_SESSION['token'] y es utilizado por el modal de confirmación de borrado del lado cliente. La vista html/usuarios/listado.html.php muestra la tabla con las columnas ID, Nombre, Apellidos, Teléfono, Email y Acciones. El email se renderiza como enlace mailto: y cada fila incluye el botón de eliminar que abre el modal #deleteModal.

Crear usuario

El flujo de alta de un nuevo usuario sigue tres pasos:
1

Mostrar el formulario de alta

usuarios/nuevo.php inicializa $usuario como un array con todos los campos vacíos y renderiza la plantilla con $vista = 'usuarios/nuevo'. El formulario se muestra vacío porque todos los valores del array están en blanco.
$usuario = ['id' => '', 'nombre' => '', 'apellidos' => '', 'email' => '', 'telefono' => '', 'id_rol' => 0];
$titulo = 'Nuevo usuario';
$vista  = 'usuarios/nuevo';
require('../../html/plantilla.html.php');
2

Formulario compartido (formulario.html.php)

html/usuarios/formulario.html.php selecciona dinámicamente el endpoint de destino según si $usuario['id'] está vacío o no:
<form action="usuarios/<?= isset($usuario) ? 'guardar' : 'crear' ?>.php"
      method="post">
    <input readonly name="id"
           value="<?= html_input_valor($usuario, 'id') ?>">
    <input name="nombre"
           value="<?= html_input_valor($usuario, 'nombre') ?>" required>
    <input name="apellidos"
           value="<?= isset($usuario) ? $usuario['apellidos'] : '' ?>" required>
    <input type="email" name="email"
           value="<?= isset($usuario) ? $usuario['email'] : '' ?>" required>
    <input name="telefono"
           value="<?= isset($usuario) ? $usuario['telefono'] : '' ?>">
    <select name="id_rol">
        <option></option>
        <?= html_opciones(
            obtener_roles($db),
            isset($usuario) ? $usuario['id_rol'] : '',
            'id', 'nombre'
        ) ?>
    </select>
    <input type="submit" value="Guardar">
</form>
3

Insertar el registro con crear.php

usuarios/crear.php recibe los datos del formulario, inserta el registro con db_insert y redirige inmediatamente a la vista de edición del nuevo usuario. En este punto la contraseña aún no está establecida; debe configurarse desde la sección de cambio de contraseña en la vista de edición:
if (empty($_REQUEST['id'])) {
    $id = db_insert($db, 'usuarios', $_REQUEST);
    $_SESSION['mensaje']['ok'] = 'Registro creado correctamente';
}

header('Location: editar.php?id=' . $id);

Editar usuario

usuarios/editar.php recupera el registro con db_get_by_id y pasa $usuario a la misma plantilla de formulario. En este contexto el formulario envía a usuarios/guardar.php, y el controlador de guardado incluye la declaración del permiso requerido:
// editar.php (fragmento)
$id      = $_REQUEST['id'];
$usuario = db_get_by_id($db, 'usuarios', $id);
$titulo  = 'Editar usuario';
$vista   = 'usuarios/editar';
require('../../html/plantilla.html.php');
// guardar.php
<?php
$permiso = 'usuarios.actualizar';
require_once('../../includes/backend.php');

$id  = $_REQUEST['id'];
$res = db_update($db, 'usuarios', $_REQUEST);
$_SESSION['mensaje']['ok'] = 'Registro guardado correctamente';

header('Location: editar.php?id=' . $id);
guardar.php actualiza todos los campos del formulario principal (nombre, apellidos, email, teléfono, id_rol) pero no modifica la contraseña. El cambio de contraseña se gestiona por separado a través de guardar_password.php.

Cambio de contraseña

public/usuarios/guardar_password.php recibe el nuevo valor de contraseña en texto plano, lo convierte a hash bcrypt con password_hash y lo actualiza en la base de datos usando db_update con solo los campos id y password:
$res = db_update($db, 'usuarios', [
    'id'       => $_REQUEST['id'],
    'password' => password_hash($_REQUEST['password'], PASSWORD_DEFAULT),
]);

if ($res) {
    $_SESSION['mensaje']['ok'] = 'Password actualizado correctamente';
} else {
    $_SESSION['mensaje']['ko'] = 'No se ha podido actualizar el password';
}

header('Location: editar.php?id=' . $_REQUEST['id']);
PASSWORD_DEFAULT utiliza el algoritmo bcrypt de PHP, generando un hash de 60 caracteres. Al ser un hash unidireccional, la contraseña original nunca queda almacenada en la base de datos.

Foto de perfil

public/usuarios/guardar_foto.php recibe la imagen subida a través del campo foto, la redimensiona a 100 píxeles de ancho manteniendo la proporción original mediante las funciones GD de PHP, y la guarda como JPEG con calidad 85 en public/imagenes/usuarios/{id}.jpg:
if (isset($_FILES['foto']) and isset($_POST['id'])) {
    $ruta_original = $_FILES['foto']['tmp_name'];
    $ruta_destino  = __DIR__ . '/../imagenes/usuarios/' . $_POST['id'] . '.jpg';

    // Obtener dimensiones originales
    list($ancho_orig, $alto_orig) = getimagesize($ruta_original);

    // Calcular el alto proporcional para un ancho de 100px
    $nuevo_ancho = 100;
    $nuevo_alto  = ($alto_orig / $ancho_orig) * $nuevo_ancho;

    // Crear los lienzos en memoria
    $lienzo_destino = imagecreatetruecolor($nuevo_ancho, $nuevo_alto);
    $imagen_origen  = imagecreatefromjpeg($ruta_original);

    // Redimensionar con resampling de alta calidad
    imagecopyresampled(
        $lienzo_destino, $imagen_origen,
        0, 0, 0, 0,
        $nuevo_ancho, $nuevo_alto,
        $ancho_orig, $alto_orig
    );

    // Guardar en disco con calidad del 85%
    imagejpeg($lienzo_destino, $ruta_destino, 85);

    // Liberar la memoria RAM
    imagedestroy($imagen_origen);
    imagedestroy($lienzo_destino);

    header('Location: editar.php?id=' . $_POST['id']);
    exit;
}
Los pasos del proceso son:
PasoFunción PHPDescripción
1getimagesize()Lee las dimensiones originales del archivo temporal subido
2Cálculo proporcionalCalcula el alto destino para mantener el ratio de aspecto
3imagecreatetruecolor()Crea un lienzo en memoria de 100×alto píxeles
3imagecreatefromjpeg()Carga la imagen original desde el archivo temporal
4imagecopyresampled()Escala con interpolación bicúbica (mayor calidad que imagecopyresized)
5imagejpeg()Serializa el lienzo destino a JPEG con calidad 85/100
6imagedestroy()Libera los recursos GD de la memoria
La imagen de entrada debe ser un JPEG, ya que el script usa imagecreatefromjpeg(). Si se requiere soporte para PNG u otros formatos habría que añadir detección del tipo MIME con mime_content_type() o exif_imagetype() y usar la función GD correspondiente.

Borrado

public/usuarios/borrar.php elimina el registro con db_delete_by_id y redirige al listado:
db_delete_by_id($db, 'usuarios', $_REQUEST['id']);
header('Location: .');
El borrado siempre está precedido por el modal de confirmación #deleteModal (definido en html/confirmacion.borrado.html.php), que incluye el campo oculto csrf_token con el valor de $_SESSION['csrf_token']. El campo se envía en el formulario, pero el servidor (borrar.php) no lo valida en la versión actual del código; la protección CSRF es sólo del lado cliente.

Permisos requeridos

El módulo de usuarios implementa verificación de permisos mediante la variable $permiso declarada antes de cargar el backend:
ArchivoPermiso requerido
index.phpusuarios.consulta
guardar.phpusuarios.actualizar
El sistema de backend comprueba que el usuario autenticado tenga ese permiso en $_SESSION['permisos'] antes de continuar la ejecución. Si no lo tiene, la petición se deniega.

Build docs developers (and LLMs) love