A continuación encontrarás una colección de operaciones masivas que se pueden iniciar desde la línea de comandos. Necesitarás acceso SSH; si eres un cliente alojado, deberás contactar al equipo de Discourse para ejecutar estos comandos.
Antes de trabajar con la consola, es extremadamente importante que tengas una copia de seguridad reciente. ¡Los errores siempre pueden ocurrir!
Lo primero que debes hacer es entrar en el contenedor de tu sitio:
cd /var/discourse
./launcher enter app
Guías adicionales:
- Performing bulk actions as a moderator
- ¿Cómo establezco los valores predeterminados históricos del nivel de seguimiento de etiquetas?
- (Obsolete) Set category tracking level defaults historically
- Change ownership of all posts by a specific user
- Replace a string in all posts
- Edit a user preference for everyone or a subset of users
- Modify trust level for all users
- Apply auto-close to existing topics
- Logout all users through the rails console
- Convert all existing topics in category to wikis
Cambiar el estado de los temas
Antes de ejecutar los siguientes comandos, ejecuta rails c para entrar en la consola.
-
Ocultar todos los temas dentro de una categoría (excluye la acción de publicación)
Puedes reemplazar
visibleporclosedoarchivedy ajustar los valores true/false según sea necesariocat_id = Category.find_by_slug('admins').id Topic.where(category_id: cat_id, visible: true).update_all(visible: false) -
Ocultar todos los temas dentro de una categoría (incluye la acción de publicación)
cat_id = Category.find_by_slug("admins").id Topic.where(category_id: cat_id, visible: true).find_each do |topic| topic.update_status('visible', false, Discourse.system_user) end -
Cerrar todos los temas creados antes de una fecha específica (incluye la acción de publicación)
Topic.where(closed: false).where("created_at <= '2015-01-01'").find_each do |topic| topic.update_status('closed', true, Discourse.system_user) end
Mover temas
Mover una colección de temas de una categoría a otra
rails c
topic_ids = [12,16,29]
cat_to = Category.find_by_slug('faq')
Topic.where(id: topic_ids).update_all(category_id: cat_to.id)
Category.update_stats
Usuarios
Eliminar un subconjunto de usuarios
Eliminar usuarios que nunca han publicado y no han visitado el sitio desde una fecha específica
rails c
User.joins(:user_stat).where("user_stats.post_count = 0 AND previous_visit_at <= '2016-05-20'::timestamp").destroy_all
Suspender un conjunto de usuarios según criterios
Establecer quién será registrado como el responsable de la suspensión de los usuarios
rails c
logger = StaffActionLogger.new(User.find_by(username_lower: "tshenry"))
Crear un período de suspensión y una razón
suspend_till = DateTime.new(2057,12,31)
reason = 'Curso completado'
En este ejemplo, nuestros criterios de usuario serán la pertenencia a un grupo.
target_group = Group.find_by_name("summer_students")
users = User.joins(:group_users).where(group_users: {group_id: target_group.id})
Suspender cada usuario según los valores establecidos anteriormente:
users.find_each do |u|
u.suspended_till = suspend_till
u.suspended_at = DateTime.now
u.save!
logger.log_user_suspend(u,reason)
putc '.'
end
Actualizar las razones de suspensión de usuarios
Quizás suspendiste usuarios que completaron un curso (véase el ejemplo arriba), y ahora quieres añadir el año del curso ya que has impartido clases durante varios años.
UserHistory.where(action: 10, details: "Curso completado").update_all(details: "Curso 2018 completado")
Levantar la suspensión de usuarios
Si necesitas levantar la suspensión de usuarios en masa, por ejemplo, porque formaban parte de la cohorte del año anterior y regresan este año, puedes hacerlo como se muestra a continuación. En el ejemplo, estamos buscando usuarios por su ID.
user_list = [1, 3, 5, 7, 11]
users = User.where("id in (?)", user_list)
users.each do |user|
user.suspended_till = nil
user.suspended_at = nil
user.save!
StaffActionLogger.new(User.find(-1)).log_user_unsuspend(user)
DiscourseEvent.trigger(:user_unsuspended, user: user)
end
Exportar/Importar
Exportar/Importar toda la configuración del sitio
Para simplemente imprimir todas las configuraciones que han sido modificadas en tu sitio, ejecuta:
rake site_settings:export
Si quieres exportar la configuración a un archivo:
rake site_settings:export > saved_settings.yml
Si quieres importar la configuración desde un archivo:
rake site_settings:import < saved_settings.yml
Exportar/Importar categorías
Hay dos opciones para exportar y un método para importar.
Exportar un conjunto de categorías completas
Primero obtén una lista de los IDs de tus categorías:
rake categories:list
Luego, separa los IDs de las categorías con espacios en la tarea rake de exportación. Por ejemplo:
rake export:categories["12 6"]
Exportar la estructura de categorías de tu sitio
Esto es esencialmente copiar el “esqueleto” de tu sitio Discourse. Incluye cada categoría junto con cualquier grupo asociado a los permisos de categoría existentes. No incluye temas:
rake export:category_structure
Si quieres la estructura de categorías junto con cualquier grupo asociado a los permisos de categoría y cualquier miembro de esos grupos:
rake export:category_structure[true]
Importar un archivo de categoría
Usa el nombre del archivo exportado como en el ejemplo a continuación:
rake import:file["category-export-2019-05-16-052430.json"]
Exportar/Importar grupos
Exportar todos los grupos de usuarios
rake export:groups
Exportar todos los grupos de usuarios incluyendo usuarios
rake export:groups[true]
Importar un archivo de grupo
Usa el nombre del archivo exportado como en el ejemplo a continuación:
rake import:file["group-export-2019-05-16-052430.json"]
Establecer permisos para múltiples categorías
Ten en cuenta que esto eliminará cualquier restricción de acceso existente que hayas configurado para las categorías involucradas. Asegúrate de incluir todos los permisos relevantes.
-
Obtén una lista de categorías junto con sus IDs
rails c Category.all.pluck("name", "id") -
Crea un array con los IDs de las categorías que deseas objetivo.
category_ids = [6,7,8,10] -
Cambia los permisos. La función
set_permissionspuede utilizar los siguientes parámetros::full,:create_post,:readonly-
Un solo permiso. Por ejemplo, hacer que un conjunto de categorías sea solo para personal:
Category.where(id: category_ids).find_each do |category| category.set_permissions(:staff => :full) category.save! end -
Múltiples permisos. Por ejemplo, hacer que un conjunto de categorías sea de solo lectura para usuarios normales:
Category.where(id: category_ids).find_each do |category| category.set_permissions(:everyone => :readonly, :staff => :full) category.save! end -
Permisos de grupo de usuarios. Por ejemplo, otorgar permisos completos a un grupo y solo lectura a otro grupo para un conjunto de categorías:
artists_group = Group.find_by_name("artists") buyers_group = Group.find_by_name("buyers") Category.where(id: category_ids).find_each do |category| category.set_permissions(artists_group.id => :full, buyers_group.id => :readonly) category.save! end
-
Etiquetar masivamente todos los temas basándose en una palabra clave
El siguiente script te permitirá etiquetar temas basándose en la presencia de una palabra clave en el título del tema o en sus publicaciones. Comienza creando un array de palabras clave:
rails c
keywords = ['manzanas','naranjas']
A continuación, necesitamos definir un método:
def tag_by_keyword(word, tag_name)
tag = Tag.find_by_name(tag_name) || Tag.create(name: tag_name)
keyword_topics = Topic.joins(:posts).where("topics.title ~* :keyword or posts.raw ~* :keyword", keyword: "\\y#{word}\\y").distinct
keyword_topics.each do |topic|
if topic.tags.exclude?(tag)
topic.tags << tag
end
end
end
Y finalmente ejecuta cada palabra clave a través del método. El siguiente etiquetará cada tema relevante con una etiqueta llamada „fruta":
keywords.each { |word| tag_by_keyword(word, 'fruta') }
Etiquetar masivamente todos los temas dentro de una categoría
Plantilla: rake tags:bulk_tag_category["<etiqueta>|<etiqueta>",<id_categoria>]
Esto sería particularmente útil al intentar convertir una categoría en una etiqueta.
Primero, usa la siguiente tarea rake para encontrar el ID de la categoría relevante.
rake categories:list
Etiqueta todos los temas de la categoría que especifiques. En este ejemplo, estarías etiquetando todos los temas en la categoría con un ID de 6 con la etiqueta „soporte“.
esto eliminará todas las demás etiquetas de cada tema.
rake tags:bulk_tag_category["soporte",6]
Añade todas las etiquetas a todos los temas de la categoría que especifiques. En este ejemplo, estarías añadiendo la etiqueta „soporte“ a todos los temas en la categoría con un ID de 6, manteniendo las etiquetas existentes.
rake tags:bulk_tag_category["soporte",6,true]
Mover todos los temas con una etiqueta específica a una sola categoría
Al intentar reestructurar tu sitio Discourse, es posible que desees mover una colección de temas sin generar ninguna notificación. Una forma de hacerlo es crear una etiqueta temporal, aplicarla a los temas adecuados, mover los temas a una categoría específica usando el código a continuación y, finalmente, eliminar la etiqueta temporal.
Obtén la etiqueta.
rails c
tag = Tag.find_by_name("tutorial")
Obtén la categoría de destino.
- Para categorías regulares:
cat_to = Category.find_by_slug('guías')
- Para subcategorías:
cat_to = Category.find_by_slug('slug-hijo','slug-padre')
Mueve los temas etiquetados a la categoría de destino.
Topic.joins(:topic_tags).where("topic_tags.tag_id = ?", tag.id).update_all(category_id: cat_to.id)
Actualiza los conteos de temas de las categorías afectadas.
Category.update_stats
CategoryTagStat.update_topic_counts
Mover todos los temas de una categoría a otra
Encuentra los IDs de categoría con la siguiente tarea rake:
rake categories:list
El primer valor debe ser el ID de la categoría de origen. El segundo valor debe ser el ID de la categoría de destino.
rake categories:move_topics[15,6]
Script de consola Rails
cat_from_id = XX # Categoría de la que mover los temas
cat_to_id = XX # Categoría a la que mover los temas
Topic.where(category_id: cat_from_id).update_all(category_id: cat_to_id)
Category.update_stats
CategoryTagStat.update_topic_counts
Cambiar el propietario de todos los temas en categorías
Encuentra los IDs de categoría con la siguiente tarea rake:
rake categories:list
Especifica el nuevo propietario y las categorías sobre las que operar. Las categorías deben ser un array de IDs de categoría; las categorías 1, 2 y 3 en el ejemplo:
rails c
user = User.find_by(username_lower: "nombre-de-usuario-minúsculas")
categories = [1, 2, 3]
Obtén todos los IDs de tema para las categorías dadas y cambia el propietario de la primera publicación en todos los temas coincidentes.
topics = Topic.where(category_id: categories).pluck(:id)
topics.each do |topic|
PostOwnerChanger.new(
post_ids: Post.where(topic_id: topic).where(post_number: 1).pluck(:id),
topic_id: topic,
new_owner: user,
acting_user: Discourse.system_user,
skip_revision: true
).change_owner!
end
Otorgar una insignia a todos los miembros de un grupo
Otorga una insignia a todos los usuarios que pertenecen a un grupo específico. El primer valor es el ID del grupo y el segundo es el ID de la insignia.
rails c
Group.find_by_name("participantes_evento").id
Badge.find_by_name("insignia_evento").id
exit
rake groups:grant_badge[42,102]
Ten en cuenta que la tarea rake anterior solo otorga una insignia; no revocará una insignia otorgada previamente si un usuario ya no forma parte del grupo especificado. Si necesitas revocar masivamente insignias para todos los usuarios que ya no forman parte de un grupo, puedes ejecutar lo siguiente:
rails c
badge_id = Badge.find_by_name("Miembro de algún grupo").id
group = Group.find_by_name("Algún_grupo")
group_user_id = group.users.pluck("id")
userBadge = UserBadge.where.not(user_id: group_user_id).where(badge_id: badge_id)
userBadge.each do |ub|
BadgeGranter.revoke(ub, revoked_by: Discourse.system_user)
end
exit
Asegurar que todos los usuarios estén en su nivel de confianza automático
Digamos que configuraste el nivel de confianza predeterminado para usuarios nuevos o invitados a un valor que no está funcionando como esperabas (como TL4). Ahora quieres cambiarlo para que tus usuarios estén en el nivel de confianza que les correspondería automáticamente, dados sus estadísticas actuales. Los siguientes comandos asegurarán que todos los usuarios estén en el nivel de confianza que deberían según Understanding Discourse Trust Levels. Nota: los usuarios con niveles de confianza bloqueados no se verán afectados.
Asegúrate de que todos los usuarios tengan el nivel de confianza correcto:
rails c
User.all.find_each do |user|
Promotion.recalculate(user)
end
Actualiza las estadísticas de los grupos para reflejar los cambios:
Group.ensure_consistency!
Scripts de mantenimiento de temas
Los siguientes scripts de Ruby demuestran cómo realizar mantenimiento automatizado en temas basándose en fechas de actividad y otros criterios. Estos scripts combinan consultas SQL para identificar temas con código Ruby para realizar acciones sobre ellos y deben ejecutarse a través de la consola de Rails de tu sitio.
Cada script sigue un patrón similar:
- Una consulta SQL que identifica los temas relevantes
- Código Ruby que procesa cada tema y aplica las acciones deseadas
- Manejo básico de errores y registro
Estos scripts se pueden personalizar mediante:
- Ajustar períodos de tiempo (por ejemplo, „6 MESES“, „1 AÑO“, „2 AÑOS")
- Cambiar selecciones de categorías para coincidir con la estructura de tu foro
- Modificar qué acciones tomar (cerrar, ocultar o mover)
- Añadir condiciones adicionales como conteos de publicaciones o umbrales de vistas
Cerrar, ocultar y mover temas inactivos
Este script identifica temas que cumplen los siguientes criterios:
- En una categoría específica
- Abiertos
- Sin resolver (usando el plugin Discourse Solved)
- Sin actividad reciente dentro de un período de tiempo específico
Luego realiza múltiples acciones:
- Los cierra,
- Los oculta y
- Los mueve a una categoría designada para contenido desactualizado
Consulta SQL
WITH topic_list AS (
SELECT ua.target_topic_id, MAX(ua.created_at) "created_at"
FROM user_actions ua
INNER JOIN topics t ON t.id = ua.target_topic_id
INNER JOIN categories c ON c.id = t.category_id
LEFT JOIN discourse_solved_solved_topics solved ON solved.topic_id = t.id
WHERE t.closed = false
AND t.category_id = [ID_CATEGORIA]
AND solved.topic_id IS NULL
AND t.deleted_at IS NULL
GROUP BY ua.target_topic_id
HAVING MAX(ua.created_at) <= (CURRENT_DATE - (INTERVAL '[PERIODO_TIEMPO]'))
ORDER BY "created_at" DESC
)
SELECT '' AS total, target_topic_id AS topic_id, created_at
FROM topic_list
UNION
SELECT ''||COUNT(*), 0, CURRENT_DATE
FROM topic_list
ORDER BY created_at DESC
SQL + Script combinados
sql = "WITH topic_list AS (
SELECT ua.target_topic_id, MAX(ua.created_at) \"created_at\"
FROM user_actions ua
INNER JOIN topics t ON t.id = ua.target_topic_id
INNER JOIN categories c ON c.id = t.category_id
LEFT JOIN discourse_solved_solved_topics solved ON solved.topic_id = t.id
WHERE t.closed = false
AND t.category_id = [ID_CATEGORIA]
AND solved.topic_id IS NULL
AND t.deleted_at IS NULL
GROUP BY ua.target_topic_id
HAVING MAX(ua.created_at) <= (CURRENT_DATE - (INTERVAL '[PERIODO_TIEMPO]'))
ORDER BY \"created_at\" DESC
)
SELECT '' AS total, target_topic_id AS topic_id, created_at
FROM topic_list
UNION
SELECT ''||COUNT(*), 0, CURRENT_DATE
FROM topic_list
ORDER BY created_at DESC"
results = ActiveRecord::Base.connection.execute(sql)
user = Discourse.system_user
destination_category = Category.find([ID_CATEGORIA_DESTINO])
puts "Se encontraron #{results.count} temas para procesar"
results.each do |row|
begin
topic = Topic.find(row["topic_id"])
# 1. Mover a la categoría de destino
topic.update!(category_id: destination_category.id)
puts "#{topic.id} movido a la categoría de destino"
# 2. Cerrar el tema
topic.update_status('closed', true, user, until: nil)
puts "#{topic.id} está cerrado"
# 3. Ocultar el tema
topic.update_status('visible', false, user, until: nil)
puts "#{topic.id} está oculto"
# Manejo de errores
rescue => e
puts "Error al procesar el tema #{row["topic_id"]}: #{e.message}"
end
end
puts "Proceso completado"
Cerrar temas resueltos sin actividad reciente
Este script cierra temas resueltos que han estado inactivos durante un período definido. Esto puede ayudar a mantener tu foro ordenado mientras se preservan los temas resueltos valiosos.
Este script identifica temas que cumplen los siguientes criterios:
- En una categoría específica
- Abiertos
- Resueltos (usando el plugin Discourse Solved)
- Sin actividad reciente dentro de un período de tiempo específico
Consulta SQL
WITH topic_list AS (
SELECT ua.target_topic_id, MAX(ua.created_at) "created_at"
FROM user_actions ua
INNER JOIN topics t ON t.id = ua.target_topic_id
INNER JOIN categories c ON c.id = t.category_id
INNER JOIN discourse_solved_solved_topics solved ON solved.topic_id = t.id
WHERE t.closed = false
AND t.category_id IN ([IDS_CATEGORIAS])
AND t.deleted_at IS NULL
GROUP BY ua.target_topic_id
HAVING MAX(ua.created_at) <= (CURRENT_DATE - (INTERVAL '[PERIODO_TIEMPO]'))
ORDER BY "created_at" DESC
)
SELECT '' AS total, target_topic_id AS topic_id, created_at
FROM topic_list
UNION
SELECT ''||COUNT(*), 0, CURRENT_DATE
FROM topic_list
ORDER BY created_at DESC
SQL + Script combinados
sql = "WITH topic_list AS (
SELECT ua.target_topic_id, MAX(ua.created_at) \"created_at\"
FROM user_actions ua
INNER JOIN topics t ON t.id = ua.target_topic_id
INNER JOIN categories c ON c.id = t.category_id
INNER JOIN discourse_solved_solved_topics solved ON solved.topic_id = t.id
WHERE t.closed = false
AND t.category_id IN ([IDS_CATEGORIAS])
AND t.deleted_at IS NULL
GROUP BY ua.target_topic_id
HAVING MAX(ua.created_at) <= (CURRENT_DATE - (INTERVAL '[PERIODO_TIEMPO]'))
ORDER BY \"created_at\" DESC
)
SELECT '' AS total, target_topic_id AS topic_id, created_at
FROM topic_list
UNION
SELECT ''||COUNT(*), 0, CURRENT_DATE
FROM topic_list
ORDER BY created_at DESC"
results = ActiveRecord::Base.connection.execute(sql)
user = Discourse.system_user
puts "Se encontraron #{results.count} temas para procesar"
results.each do |row|
begin
topic = Topic.find(row["topic_id"])
# Cerrar el tema
topic.update_status('closed', true, user, until: nil)
puts "#{topic.id} está cerrado"
# Manejo de errores
rescue => e
puts "Error al procesar el tema #{row["topic_id"]}: #{e.message}"
end
end
puts "Proceso completado"
Archivar temas previamente cerrados
Este script identifica temas que fueron cerrados previamente antes de una fecha específica y los mueve a una categoría de archivo mientras los oculta.
Consulta SQL
WITH topic_list AS (
SELECT
t.id AS topic_id,
tt.execute_at AS closed_at
FROM topics t
INNER JOIN categories c ON c.id = t.category_id
LEFT JOIN topic_timers tt ON tt.topic_id = t.id AND tt.status_type IN (1, 8)
WHERE t.closed = true
AND t.category_id IN ([IDS_CATEGORIAS])
AND t.deleted_at IS NULL
AND tt.execute_at IS NOT NULL
AND tt.execute_at <= (CURRENT_DATE - INTERVAL '[PERIODO_TIEMPO]')
ORDER BY tt.execute_at DESC
)
SELECT '' AS total, topic_id, closed_at
FROM topic_list
UNION
SELECT ''||COUNT(*), 0, CURRENT_DATE
FROM topic_list
ORDER BY closed_at DESC
SQL + Script combinados
sql = "WITH topic_list AS (
SELECT
t.id AS topic_id,
tt.execute_at AS closed_at
FROM topics t
INNER JOIN categories c ON c.id = t.category_id
LEFT JOIN topic_timers tt ON tt.topic_id = t.id AND tt.status_type IN (1, 8)
WHERE t.closed = true
AND t.category_id IN ([IDS_CATEGORIAS])
AND t.deleted_at IS NULL
AND tt.execute_at IS NOT NULL
AND tt.execute_at <= (CURRENT_DATE - INTERVAL '[PERIODO_TIEMPO]')
ORDER BY tt.execute_at DESC
)
SELECT '' AS total, topic_id, closed_at
FROM topic_list
UNION
SELECT ''||COUNT(*), 0, CURRENT_DATE
FROM topic_list
ORDER BY closed_at DESC"
results = ActiveRecord::Base.connection.execute(sql)
user = Discourse.system_user
archive_category = Category.find([ID_CATEGORIA_ARCHIVO])
puts "Se encontraron #{results.count} temas para procesar"
results.each do |row|
begin
topic = Topic.find(row["topic_id"])
# 1. Mover a la categoría de archivo
topic.update!(category_id: archive_category.id)
puts "#{topic.id} movido a la categoría de archivo"
# 2. Ocultar el tema
topic.update_status('visible', false, user, until: nil)
puts "#{topic.id} está oculto"
# Manejo de errores
rescue => e
puts "Error al procesar el tema #{row["topic_id"]}: #{e.message}"
end
end
puts "Proceso completado"
Tareas rake destructivas
Eliminar categorías completas
Lo siguiente te permitirá destruir múltiples categorías, junto con cualquier subcategoría y tema que pertenezca a esas categorías.
Imprimir una lista de IDs de categoría
rake categories:list
Destruir un conjunto de categorías basado en su ID
rake destroy:categories[10,11,12,18,30]
Eliminar todos los temas en una categoría
Eliminar todos los mensajes personales
rake destroy:private_messages
Destruir todos los grupos
rake destroy:groups
Destruir todos los usuarios no administradores
rake destroy:users
Destruir estadísticas del sitio
rake destroy:stats
Anonimizar a todos los usuarios excepto al personal
rake users:anonymize_all
Eliminar permanentemente un conjunto de publicaciones
La siguiente tarea rake eliminará permanentemente una lista de publicaciones basada en su ID. Si una publicación es la primera en un tema, todas las publicaciones en ese tema serán eliminadas permanentemente. Antes de poder ejecutar la tarea con éxito, la configuración del sitio can_permanently_delete debe estar habilitada.
Una vez que una publicación sea eliminada por esta tarea, ya no existirá en la base de datos y no se podrá recuperar.
Hay dos enfoques posibles:
-
Opción 1 – Pasar una lista separada por comas de IDs de publicación como argumento
rake destroy:posts[4,8,15,16,23,42] -
Opción 2 – Especificar un archivo de texto con una lista separada por comas de IDs de publicación (ideal para grandes conjuntos de publicaciones).
cat post_ids.txt | rake destroy:posts
He intentado incluir las tareas rake más útiles en este tema, pero hay muchas otras empaquetadas con Discourse. Si deseas ver una lista completa, puedes usar lo siguiente:
Todas las tareas que tienen descripciones
rake --tasks
Todas las tareas, incluyendo aquellas que no tienen descripciones
rake -AT