Deprecar el uso de `user.groups` en el modelo `User` en JavaScript

Esto está relacionado tanto con The road to stable, then permanent, for granular_anonymous_and_logged_in_groups_permissions como con Granular group-based permissions for anonymous and logged in users

En el núcleo, así como en muchos temas y plugins, un patrón como este se ha vuelto bastante común:

const groupIds = this.currentUser.groups.map((g) => g.id);
const allowedGroupIds = this.siteSettings.some_group_setting.split("|").map((groupId) => parseInt(groupId, 10));

const hasPermission = allowedGroups.some((groupId) =>
  userGroupIds.includes(groupId)
);

if (!hasPermission) {
  return;
}

Sin embargo, esta no es una forma efectiva de verificar los permisos de usuario. Un usuario puede ser miembro de grupos que no son visibles para él, por lo que no se serializan en el cliente y, por lo tanto, no pueden usarse de manera consistente o precisa para las comprobaciones de seguridad.

Para hacer esto más evidente, estamos renombrando currentUser.groups/user.groups a currentUser.visibleGroups/user.visibleGroups en el modelo User y deprecando la propiedad antigua. El PR inicial para hacer esto es DEV: Deprecate calling user.groups on client directly - Pull Request #42711 - discourse/discourse - GitHub .

Hay varias alternativas si necesitas verificar el permiso de un usuario basado en una lista de IDs de grupo en el cliente en JavaScript:

Para plugins

Extiende el serializador current_user con un nuevo atributo y verifica los permisos del usuario con scope.in_any_groups? en el lado del servidor, lo cual también cubre los pseudogrupos como logged_in_users y anonymous_users:

add_to_serializer(
  :current_user,
  :has_some_permission,
  include_condition: -> do
    SiteSetting.plugin_enabled
  end,
) { scope.in_any_groups?(SiteSetting.group_list_setting_map) }

Luego puedes hacer this.currentUser.has_some_permission en el cliente.

Para temas y componentes

Para los ajustes de tema de tipo list con list_type: group, puedes usar resolve_group_membership: true:

copy_button_allowed_groups:
  default: "1|3"
  type: list
  list_type: group
  resolve_group_membership: true

Esto reemplazará settings.copy_button_allowed_groups en el cliente con settings.user_in_copy_button_allowed_groups (prefijando el ajuste con user_in_), que es un booleano calculado en el lado del servidor basado en las membresías de grupo del usuario.

Esto también funciona para ajustes de objeto con type: groups . Agrega resolve_group_membership: true a la propiedad groups:

menu_sections:
  type: objects
  default:
    - name: section 1
      groups:
        - 1
        - 3
  schema:
    name: menu section
    properties:
      name:
        type: string
      groups:
        type: groups
        resolve_group_membership: true

Luego, el acceso se ve así:

for (const section of settings.menu_sections) {
  if (section.user_in_groups) {
    // El usuario está en al menos uno de los grupos seleccionados para esta sección.
  }
}