WP Manifestindependent plugin directory
manifest / users / bulk-user-deleter

Bulk User Deleter

Eliminación masiva de usuarios desde un archivo CSV o XLSX, con análisis previo obligatorio, transferencia de contenido y reporte detallado exportable.

by VLA · github.com/iramosd/bulk-user-deleter · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/iramosd/bulk-user-deleter/archive/refs/heads/main.zip

Plugin de WordPress para eliminación masiva de usuarios a partir de un archivo CSV o XLSX, con análisis previo obligatorio, transferencia de contenido y reporte detallado exportable.

  • Compatible con WordPress 6.5+ / 7.x, PHP 7.4+.
  • Sin dependencias externas: el lector XLSX usa solo las extensiones zip y xml.
  • Procesamiento por lotes vía AJAX (sin timeouts ni agotamiento de memoria).

Instalación

  1. Copiar la carpeta bulk-user-deleter/ en wp-content/plugins/.
  2. Activar Bulk User Deleter en Plugins.
  3. Abrir Usuarios → Eliminación masiva.

Requiere la capacidad delete_users (administrador por defecto). Filtrable con bud_required_capability.

Formato del archivo

Columnas reconocidas (encabezado en la primera fila, en cualquier orden):

Columna Alias aceptados
username user_login, user, login, usuario, nombre_usuario
email user_email, e-mail, mail, correo, correo_electronico
  • Delimitador CSV autodetectado (, ; tab |), BOM y codificación Latin‑1 tolerados.
  • Si no hay encabezado reconocible, se asume username, email y la primera fila se trata como dato.
  • Filas vacías ignoradas; espacios al inicio/final recortados.
  • Archivos .xls (binario antiguo) no soportados: convertir a .xlsx o .csv.

Ejemplo en samples/usuarios-ejemplo.csv.

Flujo

Paso 1 — Análisis (no elimina nada)

Por cada fila: se busca el usuario por username; si está vacío o no existe, por email; si tampoco existe se marca No encontrado. La búsqueda es case‑insensitive (colación ci de MySQL) y se resuelve en una sola consulta por lote.

Se reporta: encontrados, no encontrados, duplicados (misma fila repetida o mismo usuario en dos filas), administradores, con contenido, sin contenido.

Paso 2 — Validación de contenido

Se cuentan los elementos cuyo post_author es el usuario, en todos los tipos de contenido registrados (posts, pages, CPT, adjuntos), excluyendo tipos internos (revision, nav_menu_item, plantillas del editor de bloques…) y borradores automáticos.

Si al menos un usuario tiene contenido, la ejecución queda bloqueada hasta seleccionar un usuario activo destino (dropdown con usuarios que pueden editar contenido). El destino no puede formar parte de la lista de eliminación.

Paso 3 — Ejecución

Por cada usuario pendiente, en lotes de 5 registros por petición:

  1. Se revalida que el usuario exista y no esté protegido (los roles pueden haber cambiado desde el análisis).
  2. Sin contenido → wp_delete_user() directo.
  3. Con contenido → se transfiere con wp_update_post() (dispara los hooks y limpia cachés), se verifica que no quede nada asignado y solo entonces se elimina con wp_delete_user( $id, $destino ), que además reasigna enlaces y cualquier tipo excluido del conteo.
  4. Se verifica que el usuario ya no exista.

Un error individual marca la fila como Error y el proceso continúa con la siguiente.

Reglas de protección

  • Los administradores no se eliminan (rol administrator, manage_options o super admin de red). Se marcan como Omitido.
  • La cuenta con la que se ejecuta el proceso nunca se elimina.
  • Se puede endurecer o relajar con el filtro bud_inspect_user.

Reporte

Resumen con totales (registros, encontrados, no encontrados, duplicados, administradores, con/sin contenido, eliminados, contenido transferido, omitidos, errores) más tabla paginada y filtrable por estado, con Username | Email | Estado | Motivo | Contenido.

Exportable a CSV (admin-post.php?action=bud_export_report, protegido por nonce y capacidad). Los valores se neutralizan contra inyección de fórmulas.

Seguridad

  • current_user_can() en la página, en cada endpoint AJAX y en la exportación.
  • Nonce bud_ajax en cada petición (check_ajax_referer) y bud_export_report en la descarga.
  • Solo POST en los endpoints; el archivo se valida con is_uploaded_file(), extensión, tamaño (wp_max_upload_size()) y wp_check_filetype_and_ext(), se lee desde el temporal y nunca se guarda en uploads.
  • Todas las consultas con $wpdb->prepare(); toda la salida escapada (esc_html, esc_url, y textContent en JS).
  • XML del XLSX parseado con LIBXML_NONET (sin entidades remotas).
  • Los datos del trabajo viven en transients con TTL de 6 h y solo son legibles por quien los creó.

Filtros disponibles

Filtro Por defecto Uso
bud_required_capability delete_users Capacidad requerida
bud_max_rows 50000 Filas máximas leídas por archivo
bud_analyze_batch_size 100 Registros analizados por petición
bud_delete_batch_size 5 Registros eliminados por petición
bud_request_time_limit 20 Límite suave (s) por petición de eliminación
bud_content_post_types tipos registrados Tipos considerados «contenido»
bud_inspect_user Verdicto de protección por usuario
bud_job_ttl 6 * HOUR_IN_SECONDS Vigencia del análisis

Acción bud_job_finished al terminar un proceso (recibe el meta con las estadísticas finales).

Notas y límites conocidos

  • Multisitio: el usuario se quita del sitio actual con remove_user_from_blog() (equivalente por sitio) y su contenido se transfiere; la cuenta de red no se elimina.
  • Los comentarios conservan su user_id; solo se transfiere contenido de wp_posts (WordPress tampoco reasigna comentarios en wp_delete_user()).
  • La transferencia recorre hasta 100 000 elementos por usuario.
  • Con bud_delete_batch_size alto y usuarios con mucho contenido, el límite suave de tiempo corta el lote y el siguiente request continúa donde quedó.
  • Recomendación: respaldar la base de datos antes de una eliminación masiva.