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
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}, %{ def delete_account(_parent, %{user_id: user_id}, %{
context: %{ context: %{
current_user: %User{role: role}, current_user: %User{role: role},

View File

@@ -467,6 +467,13 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
resolve(&User.delete_account/3) resolve(&User.delete_account/3)
end 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" @desc "Set user settings"
field :set_user_settings, :user_settings do field :set_user_settings, :user_settings do
arg(:timezone, :timezone, description: "The timezone for this user") 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] @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 """ @doc """
Deletes an user. 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` export const CURRENT_USER_CLIENT = gql`
query CurrentUserClient { query CurrentUserClient {
currentUser @client { currentUser @client {

View File

@@ -370,6 +370,7 @@
"Do not receive any mail": "Do not receive any mail", "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 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 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 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_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}?", "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 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 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.", "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", "Underline": "Underline",
"Undo": "Undo", "Undo": "Undo",
"Unfollow": "Unfollow", "Unfollow": "Unfollow",

View File

@@ -368,6 +368,7 @@
"Do not receive any mail": "Ne pas recevoir d'e-mail", "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 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 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 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_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} ?", "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 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 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.", "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é", "Underline": "Souligné",
"Undo": "Annuler", "Undo": "Annuler",
"Unfollow": "Ne plus suivre", "Unfollow": "Ne plus suivre",

View File

@@ -188,12 +188,16 @@
t("Ban") t("Ban")
}}</o-button> }}</o-button>
</div> </div>
<div <div v-else>
v-else <div
class="p-4 mb-4 text-sm text-red-700 bg-red-100 rounded-lg" class="p-4 mb-4 text-sm text-red-700 bg-red-100 rounded-lg"
role="alert" role="alert"
> >
{{ t("The user has been banned") }} {{ t("The user has been banned") }}
</div>
<o-button @click="unbanAccount" variant="danger">{{
t("Unban")
}}</o-button>
</div> </div>
</td> </td>
</tr> </tr>
@@ -339,7 +343,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { formatBytes } from "@/utils/datetime"; import { formatBytes } from "@/utils/datetime";
import { ICurrentUserRole } from "@/types/enums"; 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 RouteName from "../../router/name";
import { IUser } from "../../types/current-user.model"; import { IUser } from "../../types/current-user.model";
import EmptyContent from "../../components/Utils/EmptyContent.vue"; import EmptyContent from "../../components/Utils/EmptyContent.vue";
@@ -484,6 +492,11 @@ const { mutate: deleteUserAccount } = useMutation<
{ userId: string } { userId: string }
>(DELETE_ACCOUNT_AS_MODERATOR); >(DELETE_ACCOUNT_AS_MODERATOR);
const { mutate: unbanUserAccount } = useMutation<
{ unbanProfile: { id: string } },
{ userId: string }
>(UNBAN_ACCOUNT_AS_MODERATOR);
const dialog = inject<Dialog>("dialog"); const dialog = inject<Dialog>("dialog");
const deleteAccount = async (): Promise<void> => { 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 () => { const acceptAccount = async () => {
isRoleChangeModalActive.value = false; isRoleChangeModalActive.value = false;
await updateUser({ await updateUser({