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.
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.zipPlugin 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
zipyxml. - Procesamiento por lotes vía AJAX (sin timeouts ni agotamiento de memoria).
Instalación
- Copiar la carpeta
bulk-user-deleter/enwp-content/plugins/. - Activar Bulk User Deleter en Plugins.
- 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, emaily la primera fila se trata como dato. - Filas vacías ignoradas; espacios al inicio/final recortados.
- Archivos
.xls(binario antiguo) no soportados: convertir a.xlsxo.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:
- Se revalida que el usuario exista y no esté protegido (los roles pueden haber cambiado desde el análisis).
- Sin contenido →
wp_delete_user()directo. - 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 conwp_delete_user( $id, $destino ), que además reasigna enlaces y cualquier tipo excluido del conteo. - 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_optionso 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_ajaxen cada petición (check_ajax_referer) ybud_export_reporten la descarga. - Solo POST en los endpoints; el archivo se valida con
is_uploaded_file(), extensión, tamaño (wp_max_upload_size()) ywp_check_filetype_and_ext(), se lee desde el temporal y nunca se guarda enuploads. - Todas las consultas con
$wpdb->prepare(); toda la salida escapada (esc_html,esc_url, ytextContenten 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 dewp_posts(WordPress tampoco reasigna comentarios enwp_delete_user()). - La transferencia recorre hasta 100 000 elementos por usuario.
- Con
bud_delete_batch_sizealto 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.