Possibility to unban a user

#1843
This commit is contained in:
Massedil
2025-09-27 17:54:59 +02:00
parent 2ab57db6a5
commit 9d4a558c1a
7 changed files with 92 additions and 7 deletions

View File

@@ -610,6 +610,28 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
end
end
def unban_account(_parent, %{user_id: user_id}, %{
context: %{
current_user: %User{role: role},
current_actor: %Actor{} = moderator_actor
}
})
when is_moderator(role) do
with %User{disabled: true} = user <- Users.get_user(user_id),
{:ok, %User{} = updated_user} <-
Users.unban_user(user) do
Admin.log_action(moderator_actor, "unsuspend", user)
{:ok, updated_user}
else
%User{disabled: false} ->
{:error, dgettext("errors", "User already enabled")}
end
end
def unban_account(_parent, _args, _resolution) do
{:error, dgettext("errors", "You need to be logged-in and moderator to unban an account")}
end
def delete_account(_parent, %{user_id: user_id}, %{
context: %{
current_user: %User{role: role},

View File

@@ -467,6 +467,13 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
resolve(&User.delete_account/3)
end
@desc "Unban an account"
field :unban_account, :user do
arg(:user_id, :id, description: "The user's ID")
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
resolve(&User.unban_account/3)
end
@desc "Set user settings"
field :set_user_settings, :user_settings do
arg(:timezone, :timezone, description: "The timezone for this user")

View File

@@ -170,6 +170,15 @@ defmodule Mobilizon.Users do
@delete_user_default_options [reserve_email: true]
@spec unban_user(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def unban_user(%User{} = user) do
user
|> User.changeset(%{
disabled: false
})
|> Repo.update()
end
@doc """
Deletes an user.

View File

@@ -92,6 +92,14 @@ export const DELETE_ACCOUNT_AS_MODERATOR = gql`
}
`;
export const UNBAN_ACCOUNT_AS_MODERATOR = gql`
mutation UnbanAccount($userId: ID) {
unbanAccount(userId: $userId) {
id
}
}
`;
export const CURRENT_USER_CLIENT = gql`
query CurrentUserClient {
currentUser @client {

View File

@@ -370,6 +370,7 @@
"Do not receive any mail": "Do not receive any mail",
"Do you really want to ban the account \u00ab\u00a0{emailAccount}\u00a0\u00bb\u00a0?": "Do you really want to ban the account \u00ab\u00a0{emailAccount}\u00a0\u00bb\u00a0?",
"Do you really want to ban this account? All of the user's profiles will be deleted.": "Do you really want to ban this account? All of the user's profiles will be deleted.",
"Do you really want to unban this account? The user will be able to log-in again.":"Do you really want to unban this account? The user will be able to log-in again.",
"Do you really want to suspend this profile? All of the profiles content will be deleted.": "Do you really want to suspend this profile? All of the profiles content will be deleted.",
"Do you wish to {create_event} or {explore_events}?": "Do you wish to {create_event} or {explore_events}?",
"Do you wish to {create_group} or {explore_groups}?": "Do you wish to {create_group} or {explore_groups}?",
@@ -1334,6 +1335,9 @@
"Unable to load event for participation. The error details are provided below:": "Unable to load event for participation. The error details are provided below:",
"Unable to save your participation in this browser.": "Unable to save your participation in this browser.",
"Unable to update the profile. The avatar picture may be too heavy.": "Unable to update the profile. The avatar picture may be too heavy.",
"Unban": "Unban",
"Unban the account": "Unban the account",
"Unban the account?": "Unban the account?",
"Underline": "Underline",
"Undo": "Undo",
"Unfollow": "Unfollow",

View File

@@ -368,6 +368,7 @@
"Do not receive any mail": "Ne pas recevoir d'e-mail",
"Do you really want to ban the account « {emailAccount} » ?": "Voulez-vous vraiment bannir le compte « {emailAccount} » ?",
"Do you really want to ban this account? All of the user's profiles will be deleted.": "Voulez-vous vraiment bannir ce compte ? Tous les profils de cet·te utilisateur·ice seront supprimés.",
"Do you really want to unban this account? The user will be able to log-in again.":"Voulez-vous vraiment débannir ce compte ? L'utilisateur pourra à nouveau se connecter.",
"Do you really want to suspend this profile? All of the profiles content will be deleted.": "Voulez-vous vraiment suspendre ce profil ? Tout le contenu du profil sera supprimé.",
"Do you wish to {create_event} or {explore_events}?": "Voulez-vous {create_event} ou {explore_events} ?",
"Do you wish to {create_group} or {explore_groups}?": "Voulez-vous {create_group} ou {explore_groups} ?",
@@ -1341,6 +1342,9 @@
"Unable to load event for participation. The error details are provided below:": "Impossible de charger l'événement pour la participation. Les détails de l'erreur sont disponibles ci-dessous :",
"Unable to save your participation in this browser.": "Échec de la sauvegarde de votre participation dans ce navigateur.",
"Unable to update the profile. The avatar picture may be too heavy.": "Impossible de mettre à jour le profil. L'image d'avatar est probablement trop lourde.",
"Unban": "Débannir",
"Unban the account": "Débannir le compte",
"Unban the account?": "Débannir le compte?",
"Underline": "Souligné",
"Undo": "Annuler",
"Unfollow": "Ne plus suivre",

View File

@@ -188,12 +188,16 @@
t("Ban")
}}</o-button>
</div>
<div
v-else
class="p-4 mb-4 text-sm text-red-700 bg-red-100 rounded-lg"
role="alert"
>
{{ t("The user has been banned") }}
<div v-else>
<div
class="p-4 mb-4 text-sm text-red-700 bg-red-100 rounded-lg"
role="alert"
>
{{ t("The user has been banned") }}
</div>
<o-button @click="unbanAccount" variant="danger">{{
t("Unban")
}}</o-button>
</div>
</td>
</tr>
@@ -339,7 +343,11 @@
<script lang="ts" setup>
import { formatBytes } from "@/utils/datetime";
import { ICurrentUserRole } from "@/types/enums";
import { GET_USER, DELETE_ACCOUNT_AS_MODERATOR } from "../../graphql/user";
import {
GET_USER,
DELETE_ACCOUNT_AS_MODERATOR,
UNBAN_ACCOUNT_AS_MODERATOR,
} from "../../graphql/user";
import RouteName from "../../router/name";
import { IUser } from "../../types/current-user.model";
import EmptyContent from "../../components/Utils/EmptyContent.vue";
@@ -484,6 +492,11 @@ const { mutate: deleteUserAccount } = useMutation<
{ userId: string }
>(DELETE_ACCOUNT_AS_MODERATOR);
const { mutate: unbanUserAccount } = useMutation<
{ unbanProfile: { id: string } },
{ userId: string }
>(UNBAN_ACCOUNT_AS_MODERATOR);
const dialog = inject<Dialog>("dialog");
const deleteAccount = async (): Promise<void> => {
@@ -504,6 +517,24 @@ const deleteAccount = async (): Promise<void> => {
});
};
const unbanAccount = async (): Promise<void> => {
dialog?.confirm({
title: t("Unban the account?"),
message: t(
"Do you really want to unban this account? The user will be able to log-in again."
),
confirmText: t("Unban the account"),
cancelText: t("Cancel"),
variant: "danger",
onConfirm: async () => {
unbanUserAccount({
userId: props.id,
});
return router.push({ name: RouteName.USERS });
},
});
};
const acceptAccount = async () => {
isRoleChangeModalActive.value = false;
await updateUser({