Merge branch 'more-fixes' into 'master'
Even more fixes See merge request framasoft/mobilizon!472
This commit is contained in:
commit
061f51447e
|
@ -7,31 +7,11 @@
|
|||
<date-calendar-icon :date="participation.event.beginsOn" />
|
||||
</div>
|
||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: participation.event.uuid } }">
|
||||
<h2 class="title">{{ participation.event.title }}</h2>
|
||||
<h3 class="title">{{ participation.event.title }}</h3>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="participation-actor has-text-grey">
|
||||
<span
|
||||
v-if="
|
||||
participation.event.physicalAddress && participation.event.physicalAddress.locality
|
||||
"
|
||||
>{{ participation.event.physicalAddress.locality }} -</span
|
||||
>
|
||||
<span>
|
||||
<span>
|
||||
{{
|
||||
$t("Organized by {name}", {
|
||||
name: participation.event.organizerActor.displayName(),
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span v-if="participation.role === ParticipantRole.PARTICIPANT">
|
||||
{{ $t("Going as {name}", { name: participation.actor.displayName() }) }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<span class="column is-narrow">
|
||||
<b-icon icon="earth" v-if="participation.event.visibility === EventVisibility.PUBLIC" />
|
||||
<b-icon
|
||||
icon="link"
|
||||
|
@ -42,7 +22,42 @@
|
|||
v-else-if="participation.event.visibility === EventVisibility.PRIVATE"
|
||||
/>
|
||||
</span>
|
||||
<span class="column is-narrow participant-stats">
|
||||
<span
|
||||
v-if="
|
||||
participation.event.physicalAddress && participation.event.physicalAddress.locality
|
||||
"
|
||||
>{{ participation.event.physicalAddress.locality }} -</span
|
||||
>
|
||||
<span>
|
||||
<i18n tag="span" path="Organized by {name}">
|
||||
<popover-actor-card
|
||||
slot="name"
|
||||
:actor="participation.event.organizerActor"
|
||||
:inline="true"
|
||||
>
|
||||
{{ participation.event.organizerActor.displayName() }}
|
||||
</popover-actor-card>
|
||||
</i18n>
|
||||
<i18n
|
||||
v-if="participation.role === ParticipantRole.PARTICIPANT"
|
||||
path="Going as {name}"
|
||||
tag="span"
|
||||
>
|
||||
<popover-actor-card slot="name" :actor="participation.actor" :inline="true">
|
||||
{{ participation.actor.displayName() }}
|
||||
</popover-actor-card>
|
||||
</i18n>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
class="participant-stats"
|
||||
v-if="
|
||||
![ParticipantRole.PARTICIPANT, ParticipantRole.NOT_APPROVED].includes(
|
||||
participation.role
|
||||
)
|
||||
"
|
||||
>
|
||||
<span v-if="participation.event.options.maximumAttendeeCapacity !== 0">
|
||||
{{
|
||||
$t("{approved} / {total} seats", {
|
||||
|
@ -176,6 +191,7 @@ import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
|
|||
import EventMixin from "../../mixins/event";
|
||||
import RouteName from "../../router/name";
|
||||
import { changeIdentity } from "../../utils/auth";
|
||||
import PopoverActorCard from "../Account/PopoverActorCard.vue";
|
||||
|
||||
const defaultOptions: IEventCardOptions = {
|
||||
hideDate: true,
|
||||
|
@ -187,6 +203,7 @@ const defaultOptions: IEventCardOptions = {
|
|||
@Component({
|
||||
components: {
|
||||
DateCalendarIcon,
|
||||
PopoverActorCard,
|
||||
},
|
||||
apollo: {
|
||||
currentActor: {
|
||||
|
|
|
@ -206,6 +206,7 @@ export const LOGGED_USER_PARTICIPATIONS = gql`
|
|||
preferredUsername
|
||||
name
|
||||
domain
|
||||
summary
|
||||
avatar {
|
||||
url
|
||||
}
|
||||
|
@ -226,6 +227,7 @@ export const LOGGED_USER_PARTICIPATIONS = gql`
|
|||
preferredUsername
|
||||
name
|
||||
domain
|
||||
summary
|
||||
avatar {
|
||||
url
|
||||
}
|
||||
|
|
|
@ -184,6 +184,11 @@ export const LOGS = gql`
|
|||
domain
|
||||
name
|
||||
}
|
||||
... on User {
|
||||
id
|
||||
email
|
||||
confirmedAt
|
||||
}
|
||||
}
|
||||
insertedAt
|
||||
}
|
||||
|
|
|
@ -256,7 +256,7 @@
|
|||
"On {date} from {startTime} to {endTime}": "On {date} from {startTime} to {endTime}",
|
||||
"On {date} starting at {startTime}": "On {date} starting at {startTime}",
|
||||
"On {date}": "On {date}",
|
||||
"Only accessible through link and search (private)": "Only accessible through link and search (private)",
|
||||
"Only accessible through link (private)": "Only accessible through link (private)",
|
||||
"Only alphanumeric characters and underscores are supported.": "Only alphanumeric characters and underscores are supported.",
|
||||
"Open": "Open",
|
||||
"Opened reports": "Opened reports",
|
||||
|
@ -581,12 +581,12 @@
|
|||
"Every hour": "Every hour",
|
||||
"Every day": "Every day",
|
||||
"report #{report_number}": "report #{report_number}",
|
||||
"{actor} closed {report}": "{actor} closed {report}",
|
||||
"{moderator} closed {report}": "{moderator} closed {report}",
|
||||
"a non-existent report": "a non-existent report",
|
||||
"{actor} reopened {report}": "{actor} reopened {report}",
|
||||
"{actor} marked {report} as resolved": "{actor} marked {report} as resolved",
|
||||
"{actor} added a note on {report}": "{actor} added a note on {report}",
|
||||
"{actor} deleted an event named \"{title}\"": "{actor} deleted an event named \"{title}\"",
|
||||
"{moderator} reopened {report}": "{moderator} reopened {report}",
|
||||
"{moderator} marked {report} as resolved": "{moderator} marked {report} as resolved",
|
||||
"{moderator} added a note on {report}": "{moderator} added a note on {report}",
|
||||
"{moderator} deleted an event named \"{title}\"": "{moderator} deleted an event named \"{title}\"",
|
||||
"If the direction given by the development team does not suit you, you have the legal right to create your own version of the software, with your own governance choices.": "If the direction given by the development team does not suit you, you have the legal right to create your own version of the software, with your own governance choices.",
|
||||
"change the world, one byte at a time": "change the world, one byte at a time",
|
||||
"Concieved with care for humans": "Concieved with care for humans",
|
||||
|
@ -623,7 +623,7 @@
|
|||
"Participations": "Participations",
|
||||
"Nothing to see here": "Nothing to see here",
|
||||
"Not confirmed": "Not confirmed",
|
||||
"{actor} suspended profile {profile}": "{actor} suspended profile {profile}",
|
||||
"{moderator} suspended profile {profile}": "{moderator} suspended profile {profile}",
|
||||
"Suspend": "Suspend",
|
||||
"Unsuspend": "Unsuspend",
|
||||
"None": "None",
|
||||
|
@ -642,5 +642,7 @@
|
|||
"I agree to the {instanceRules} and {termsOfService}": "I agree to the {instanceRules} and {termsOfService}",
|
||||
"This email is already used.": "This email is already used.",
|
||||
"Powered by {mobilizon}. © 2018 - {date} The Mobilizon Contributors - Made with the financial support of {contributors}.": "Powered by {mobilizon}. © 2018 - {date} The Mobilizon Contributors - Made with the financial support of {contributors}.",
|
||||
"more than 1360 contributors": "more than 1360 contributors"
|
||||
"more than 1360 contributors": "more than 1360 contributors",
|
||||
"{moderator} has unsuspended profile {profile}": "{moderator} has unsuspended profile {profile}",
|
||||
"{moderator} has deleted user {user}": "{moderator} has deleted user {user}"
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@
|
|||
"On {date} starting at {startTime}": "Le {date} à partir de {startTime}",
|
||||
"One person is going": "Personne n'y va | Une personne y va | {approved} personnes y vont",
|
||||
"Ongoing tasks": "Tâches en cours",
|
||||
"Only accessible through link and search (private)": "Uniquement accessibles par lien et la recherche (privé)",
|
||||
"Only accessible through link (private)": "Uniquement accessible par lien (privé)",
|
||||
"Only alphanumeric characters and underscores are supported.": "Seuls les caractères alphanumériques et les tirets bas sont acceptés.",
|
||||
"Open": "Ouvert",
|
||||
"Opened reports": "Signalements ouverts",
|
||||
|
@ -603,12 +603,12 @@
|
|||
"Every hour": "À chaque heure",
|
||||
"Every day": "Chaque jour",
|
||||
"report #{report_number}": "le signalement #{report_number}",
|
||||
"{actor} closed {report}": "{actor} a fermé {report}",
|
||||
"{moderator} closed {report}": "{moderator} a fermé {report}",
|
||||
"a non-existent report": "un signalement non-existant",
|
||||
"{actor} reopened {report}": "{actor} a réouvert {report}",
|
||||
"{actor} marked {report} as resolved": "{actor} a marqué {report} comme résolu",
|
||||
"{actor} added a note on {report}": "{actor} a ajouté une note sur {report}",
|
||||
"{actor} deleted an event named \"{title}\"": "{actor} a supprimé un événement nommé \"{title}\"",
|
||||
"{moderator} reopened {report}": "{moderator} a réouvert {report}",
|
||||
"{moderator} marked {report} as resolved": "{moderator} a marqué {report} comme résolu",
|
||||
"{moderator} added a note on {report}": "{moderator} a ajouté une note sur {report}",
|
||||
"{moderator} deleted an event named \"{title}\"": "{moderator} a supprimé un événement nommé \"{title}\"",
|
||||
"If the direction given by the development team does not suit you, you have the legal right to create your own version of the software, with your own governance choices.": "Si la direction donnée par l’équipe de développement ne vous convient pas, vous avez légalement le droit de créer votre version du logiciel avec vos propres choix de gouvernance.",
|
||||
"change the world, one byte at a time": "changer le monde, un octet à la fois",
|
||||
"Concieved with care for humans": "Conçu avec soin pour les humains",
|
||||
|
@ -621,7 +621,7 @@
|
|||
"Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the fall of 2020</b>.": "Mobilizon est en cours de développement, nous ajouterons de nouvelles fonctionnalités à ce site lors de mises à jour régulières, jusqu'à la publication de <b>la version 1 du logiciel à l'automne 2020</b>.",
|
||||
"To activate more notifications, head over to the notification settings.": "Pour activer plus de notifications, rendez-vous dans vos paramètres de notification.",
|
||||
"Manage my notifications": "Gérer mes notifications",
|
||||
"We use your timezone to make sure you get notifications for an event at the correct time.": "Nous utilisons votre fuseau hoaire pour nous assurer que vous recevez les notifications pour un événement au bon moment.",
|
||||
"We use your timezone to make sure you get notifications for an event at the correct time.": "Nous utilisons votre fuseau horaire pour nous assurer que vous recevez les notifications pour un événement au bon moment.",
|
||||
"Your timezone was detected as {timezone}.": "Votre fuseau horaire a été détecté en tant que {timezone}.",
|
||||
"Manage my settings": "Gérer mes paramètres",
|
||||
"Let's define a few settings": "Définissons quelques paramètres",
|
||||
|
@ -646,7 +646,7 @@
|
|||
"Participations": "Participations",
|
||||
"Nothing to see here": "Il n'y a rien à voir ici",
|
||||
"Not confirmed": "Non confirmé·e",
|
||||
"{actor} suspended profile {profile}": "{actor} a suspendu le profil {profile}",
|
||||
"{moderator} suspended profile {profile}": "{moderator} a suspendu le profil {profile}",
|
||||
"Suspend": "Suspendre",
|
||||
"Unsuspend": "Annuler la suspension",
|
||||
"None": "Aucun",
|
||||
|
@ -665,5 +665,7 @@
|
|||
"I agree to the {instanceRules} and {termsOfService}": "J'accepte les {instanceRules} et les {termsOfService}",
|
||||
"This email is already used.": "Cette adresse email est déjà utilisée.",
|
||||
"Powered by {mobilizon}. © 2018 - {date} The Mobilizon Contributors - Made with the financial support of {contributors}.": "Propulsé par {mobilizon}. © 2018 - {date} Les contributeur·ices Mobilizon - Fait avec le soutien financier de {contributors}.",
|
||||
"more than 1360 contributors": "plus de 1360 contributeur·ices"
|
||||
"more than 1360 contributors": "plus de 1360 contributeur·ices",
|
||||
"{moderator} has unsuspended profile {profile}": "{moderator} a annulé la suspension de {profile}",
|
||||
"{moderator} has deleted user {user}": "{moderator} a supprimé l'utilisateur·ice {user}"
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ export enum ActionLogAction {
|
|||
COMMENT_DELETION = "COMMENT_DELETION",
|
||||
ACTOR_SUSPENSION = "ACTOR_SUSPENSION",
|
||||
ACTOR_UNSUSPENSION = "ACTOR_UNSUSPENSION",
|
||||
USER_DELETION = "USER_DELETION",
|
||||
}
|
||||
|
||||
export interface IActionLog {
|
||||
|
|
|
@ -35,7 +35,12 @@
|
|||
</router-link>
|
||||
</b-table-column>
|
||||
<b-table-column field="confirmedAt" :label="$t('Confirmed at')" :centered="true">
|
||||
{{ props.row.confirmedAt | formatDateTimeString }}
|
||||
<template v-if="props.row.confirmedAt">
|
||||
{{ props.row.confirmedAt | formatDateTimeString }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t("Not confirmed") }}
|
||||
</template>
|
||||
</b-table-column>
|
||||
<b-table-column field="locale" :label="$t('Language')" :centered="true">
|
||||
{{ props.row.locale }}
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
v-model="event.visibility"
|
||||
name="eventVisibility"
|
||||
:native-value="EventVisibility.UNLISTED"
|
||||
>{{ $t("Only accessible through link and search (private)") }}</b-radio
|
||||
>{{ $t("Only accessible through link (private)") }}</b-radio
|
||||
>
|
||||
</div>
|
||||
<!-- <div class="field">
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
<i18n
|
||||
v-if="log.action === ActionLogAction.REPORT_UPDATE_CLOSED"
|
||||
tag="span"
|
||||
path="{actor} closed {report}"
|
||||
path="{moderator} closed {report}"
|
||||
>
|
||||
<router-link
|
||||
slot="actor"
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
|
@ -23,10 +23,10 @@
|
|||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_OPENED"
|
||||
tag="span"
|
||||
path="{actor} reopened {report}"
|
||||
path="{moderator} reopened {report}"
|
||||
>
|
||||
<router-link
|
||||
slot="actor"
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
|
@ -39,10 +39,10 @@
|
|||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_RESOLVED"
|
||||
tag="span"
|
||||
path="{actor} marked {report} as resolved"
|
||||
path="{moderator} marked {report} as resolved"
|
||||
>
|
||||
<router-link
|
||||
slot="actor"
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
|
@ -55,10 +55,10 @@
|
|||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.NOTE_CREATION"
|
||||
tag="span"
|
||||
path="{actor} added a note on {report}"
|
||||
path="{moderator} added a note on {report}"
|
||||
>
|
||||
<router-link
|
||||
slot="actor"
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
|
@ -73,10 +73,10 @@
|
|||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.EVENT_DELETION"
|
||||
tag="span"
|
||||
path='{actor} deleted an event named "{title}"'
|
||||
path='{moderator} deleted an event named "{title}"'
|
||||
>
|
||||
<router-link
|
||||
slot="actor"
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
|
@ -85,10 +85,10 @@
|
|||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.ACTOR_SUSPENSION"
|
||||
tag="span"
|
||||
path="{actor} suspended profile {profile}"
|
||||
path="{moderator} suspended profile {profile}"
|
||||
>
|
||||
<router-link
|
||||
slot="actor"
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
|
@ -98,6 +98,40 @@
|
|||
>{{ displayNameAndUsername(log.object) }}
|
||||
</router-link>
|
||||
</i18n>
|
||||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.ACTOR_UNSUSPENSION"
|
||||
tag="span"
|
||||
path="{moderator} has unsuspended profile {profile}"
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
slot="profile"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.object.id } }"
|
||||
>{{ displayNameAndUsername(log.object) }}
|
||||
</router-link>
|
||||
</i18n>
|
||||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.USER_DELETION"
|
||||
tag="span"
|
||||
path="{moderator} has deleted user {user}"
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
v-if="log.object.confirmedAt"
|
||||
slot="user"
|
||||
:to="{ name: RouteName.ADMIN_USER_PROFILE, params: { id: log.object.id } }"
|
||||
>{{ log.object.email }}
|
||||
</router-link>
|
||||
<b v-else slot="user">{{ log.object.email }}</b>
|
||||
</i18n>
|
||||
<br />
|
||||
<small>{{ log.insertedAt | formatDateTimeString }}</small>
|
||||
</div>
|
||||
|
|
|
@ -111,6 +111,13 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
|
|||
}
|
||||
end
|
||||
|
||||
defp transform_action_log(User, :delete, %ActionLog{changes: changes}) do
|
||||
%{
|
||||
action: :user_deletion,
|
||||
object: convert_changes_to_struct(User, changes)
|
||||
}
|
||||
end
|
||||
|
||||
# Changes are stored as %{"key" => "value"} so we need to convert them back as struct
|
||||
defp convert_changes_to_struct(struct, %{"report_id" => _report_id} = changes) do
|
||||
with data <- for({key, val} <- changes, into: %{}, do: {String.to_atom(key), val}),
|
||||
|
|
|
@ -110,6 +110,29 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
|||
{:ok, stats.participant + stats.moderator + stats.administrator + stats.creator}
|
||||
end
|
||||
|
||||
def stats_participants(
|
||||
%Event{participant_stats: %EventParticipantStats{} = stats, id: event_id} = _event,
|
||||
_args,
|
||||
%{context: %{current_user: %User{id: user_id} = _user}} = _resolution
|
||||
) do
|
||||
if Events.is_user_moderator_for_event?(user_id, event_id) do
|
||||
stats =
|
||||
Map.put(
|
||||
stats,
|
||||
:going,
|
||||
stats.participant + stats.moderator + stats.administrator + stats.creator
|
||||
)
|
||||
|
||||
{:ok, stats}
|
||||
else
|
||||
{:ok, %EventParticipantStats{}}
|
||||
end
|
||||
end
|
||||
|
||||
def stats_participants(_event, _args, _resolution) do
|
||||
{:ok, %EventParticipantStats{}}
|
||||
end
|
||||
|
||||
@doc """
|
||||
List related events
|
||||
"""
|
||||
|
|
|
@ -5,7 +5,7 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
|
|||
|
||||
import Mobilizon.Users.Guards
|
||||
|
||||
alias Mobilizon.{Actors, Config, Events, Users}
|
||||
alias Mobilizon.{Actors, Admin, Config, Events, Users}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Crypto
|
||||
alias Mobilizon.Federation.ActivityPub
|
||||
|
@ -390,11 +390,20 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
|
|||
end
|
||||
|
||||
def delete_account(_parent, %{user_id: user_id}, %{
|
||||
context: %{current_user: %User{role: role}}
|
||||
context: %{current_user: %User{role: role} = moderator_user}
|
||||
})
|
||||
when is_moderator(role) do
|
||||
with %User{} = user <- Users.get_user(user_id) do
|
||||
do_delete_account(%User{} = user)
|
||||
with {:moderator_actor, %Actor{} = moderator_actor} <-
|
||||
{:moderator_actor, Users.get_actor_for_user(moderator_user)},
|
||||
%User{disabled: false} = user <- Users.get_user(user_id),
|
||||
{:ok, %User{}} <- do_delete_account(%User{} = user) do
|
||||
Admin.log_action(moderator_actor, "delete", user)
|
||||
else
|
||||
{:moderator_actor, nil} ->
|
||||
{:error, "No actor found for the moderator user"}
|
||||
|
||||
%User{disabled: true} ->
|
||||
{:error, "User already disabled"}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
|||
alias Mobilizon.Conversations.Comment
|
||||
alias Mobilizon.Events.Event
|
||||
alias Mobilizon.Reports.{Note, Report}
|
||||
alias Mobilizon.Users.User
|
||||
|
||||
alias Mobilizon.GraphQL.Resolvers.Admin
|
||||
|
||||
|
@ -32,6 +33,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
|||
value(:event_update)
|
||||
value(:actor_suspension)
|
||||
value(:actor_unsuspension)
|
||||
value(:user_deletion)
|
||||
end
|
||||
|
||||
@desc "The objects that can be in an action log"
|
||||
|
@ -54,6 +56,9 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
|||
%Actor{type: "Person"}, _ ->
|
||||
:person
|
||||
|
||||
%User{}, _ ->
|
||||
:user
|
||||
|
||||
_, _ ->
|
||||
nil
|
||||
end)
|
||||
|
|
|
@ -63,7 +63,10 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
|||
|
||||
field(:draft, :boolean, description: "Whether or not the event is a draft")
|
||||
|
||||
field(:participant_stats, :participant_stats)
|
||||
field(:participant_stats, :participant_stats,
|
||||
description: "Statistics on the event",
|
||||
resolve: &Event.stats_participants/3
|
||||
)
|
||||
|
||||
field(:participants, :paginated_participant_list, description: "The event's participants") do
|
||||
arg(:page, :integer, default_value: 1)
|
||||
|
@ -121,11 +124,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
|||
end
|
||||
|
||||
object :participant_stats do
|
||||
field(:going, :integer,
|
||||
description: "The number of approved participants",
|
||||
resolve: &Event.stats_participants_going/3
|
||||
)
|
||||
|
||||
field(:going, :integer, description: "The number of approved participants")
|
||||
field(:not_approved, :integer, description: "The number of not approved participants")
|
||||
field(:not_confirmed, :integer, description: "The number of not confirmed participants")
|
||||
field(:rejected, :integer, description: "The number of rejected participants")
|
||||
|
|
|
@ -15,7 +15,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
|||
|
||||
@desc "A local user of Mobilizon"
|
||||
object :user do
|
||||
field(:id, non_null(:id), description: "The user's ID")
|
||||
interfaces([:action_log_object])
|
||||
field(:id, :id, description: "The user's ID")
|
||||
field(:email, non_null(:string), description: "The user's email")
|
||||
|
||||
field(:actors, non_null(list_of(:person)),
|
||||
|
|
|
@ -425,6 +425,16 @@ defmodule Mobilizon.Events do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec is_user_moderator_for_event?(integer | String.t(), integer | String.t()) :: boolean
|
||||
def is_user_moderator_for_event?(user_id, event_id) do
|
||||
Participant
|
||||
|> join(:inner, [p], a in Actor, on: p.actor_id == a.id)
|
||||
|> where([p, _a], p.event_id == ^event_id)
|
||||
|> where([_p, a], a.user_id == ^user_id)
|
||||
|> where([p, _a], p.role == ^:creator)
|
||||
|> Repo.exists?()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Finds close events to coordinates.
|
||||
Radius is in meters and defaults to 50km.
|
||||
|
@ -741,7 +751,8 @@ defmodule Mobilizon.Events do
|
|||
|> Repo.one()
|
||||
end
|
||||
|
||||
@default_participant_roles [:participant, :moderator, :administrator, :creator]
|
||||
@moderator_roles [:moderator, :administrator, :creator]
|
||||
@default_participant_roles [:participant] ++ @moderator_roles
|
||||
|
||||
@doc """
|
||||
Returns the list of participants for an event.
|
||||
|
@ -810,7 +821,7 @@ defmodule Mobilizon.Events do
|
|||
where:
|
||||
p.event_id == ^event_id and
|
||||
p.actor_id ==
|
||||
^actor_id and p.role in ^[:moderator, :administrator, :creator]
|
||||
^actor_id and p.role in ^@moderator_roles
|
||||
)
|
||||
) == nil)
|
||||
end
|
||||
|
|
|
@ -554,7 +554,8 @@ defmodule Mobilizon.GraphQL.Resolvers.ParticipantTest do
|
|||
|
||||
test "stats_participants_for_event/3 give the number of (un)approved participants", %{
|
||||
conn: conn,
|
||||
actor: actor
|
||||
actor: actor,
|
||||
user: user
|
||||
} do
|
||||
event =
|
||||
@event
|
||||
|
@ -577,6 +578,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ParticipantTest do
|
|||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
||||
|
||||
assert json_response(res, 200)["data"]["event"]["uuid"] == to_string(event.uuid)
|
||||
|
@ -623,12 +625,33 @@ defmodule Mobilizon.GraphQL.Resolvers.ParticipantTest do
|
|||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
||||
|
||||
assert json_response(res, 200)["data"]["event"]["uuid"] == to_string(event.uuid)
|
||||
assert json_response(res, 200)["data"]["event"]["participantStats"]["going"] == 2
|
||||
assert json_response(res, 200)["data"]["event"]["participantStats"]["notApproved"] == 1
|
||||
assert json_response(res, 200)["data"]["event"]["participantStats"]["rejected"] == 1
|
||||
|
||||
query = """
|
||||
{
|
||||
event(uuid: "#{event.uuid}") {
|
||||
uuid,
|
||||
participantStats {
|
||||
going,
|
||||
notApproved,
|
||||
rejected
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
||||
|
||||
assert is_nil(json_response(res, 200)["errors"])
|
||||
assert json_response(res, 200)["data"]["event"]["going"] == nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue