2019-12-20 13:04:34 +01:00
|
|
|
defmodule Mobilizon.GraphQL.Resolvers.Participant do
|
|
|
|
@moduledoc """
|
|
|
|
Handles the participation-related GraphQL calls.
|
|
|
|
"""
|
2021-09-10 11:35:32 +02:00
|
|
|
alias Mobilizon.{Actors, Config, Crypto, Events}
|
2019-12-20 13:04:34 +01:00
|
|
|
alias Mobilizon.Actors.Actor
|
|
|
|
alias Mobilizon.Events.{Event, Participant}
|
|
|
|
alias Mobilizon.GraphQL.API.Participations
|
|
|
|
alias Mobilizon.Users.User
|
|
|
|
alias Mobilizon.Web.Email
|
|
|
|
alias Mobilizon.Web.Email.Checker
|
|
|
|
require Logger
|
2020-09-29 09:53:48 +02:00
|
|
|
import Mobilizon.Web.Gettext
|
2021-08-13 11:22:04 +02:00
|
|
|
import Mobilizon.GraphQL.Resolvers.Event.Utils
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
@doc """
|
2020-11-06 15:43:38 +01:00
|
|
|
Join an event for an regular or anonymous actor
|
2019-12-20 13:04:34 +01:00
|
|
|
"""
|
2021-09-28 19:40:37 +02:00
|
|
|
@spec actor_join_event(any(), map(), Absinthe.Resolution.t()) ::
|
|
|
|
{:ok, Participant.t()} | {:error, String.t()}
|
2019-12-20 13:04:34 +01:00
|
|
|
def actor_join_event(
|
|
|
|
_parent,
|
2020-03-05 19:32:34 +01:00
|
|
|
%{actor_id: actor_id, event_id: event_id} = args,
|
2019-12-20 13:04:34 +01:00
|
|
|
%{context: %{current_user: %User{} = user}}
|
|
|
|
) do
|
|
|
|
case User.owns_actor(user, actor_id) do
|
|
|
|
{:is_owned, %Actor{} = actor} ->
|
2020-03-05 19:32:34 +01:00
|
|
|
do_actor_join_event(actor, event_id, args)
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
_ ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def actor_join_event(
|
|
|
|
_parent,
|
|
|
|
%{actor_id: actor_id, event_id: event_id} = args,
|
|
|
|
_resolution
|
|
|
|
) do
|
|
|
|
with {:has_event, {:ok, %Event{} = event}} <-
|
|
|
|
{:has_event, Mobilizon.Events.get_event_with_preload(event_id)},
|
|
|
|
{:anonymous_participation_enabled, true} <-
|
|
|
|
{:anonymous_participation_enabled,
|
|
|
|
event.local == true && Config.anonymous_participation?() &&
|
|
|
|
event.options.anonymous_participation == true},
|
|
|
|
{:anonymous_actor_id, true} <-
|
|
|
|
{:anonymous_actor_id, to_string(Config.anonymous_actor_id()) == actor_id},
|
|
|
|
{:email_required, true} <-
|
|
|
|
{:email_required,
|
|
|
|
Config.anonymous_participation_email_required?() &&
|
|
|
|
args |> Map.get(:email) |> valid_email?()},
|
|
|
|
{:confirmation_token, {confirmation_token, role}} <-
|
|
|
|
{:confirmation_token,
|
|
|
|
if(Config.anonymous_participation_email_confirmation_required?(),
|
|
|
|
do: {Crypto.random_string(30), :not_confirmed},
|
|
|
|
else: {nil, :participant}
|
|
|
|
)},
|
|
|
|
# We only federate if the participation is not to be confirmed later
|
|
|
|
args <-
|
|
|
|
args
|
|
|
|
|> Map.put(:confirmation_token, confirmation_token)
|
|
|
|
|> Map.put(:cancellation_token, Crypto.random_string(30))
|
|
|
|
|> Map.put(:role, role)
|
|
|
|
|> Map.put(:local, role == :participant),
|
|
|
|
{:actor_not_found, %Actor{} = actor} <-
|
|
|
|
{:actor_not_found, Actors.get_actor_with_preload(actor_id)},
|
|
|
|
{:ok, %Participant{} = participant} <- do_actor_join_event(actor, event_id, args) do
|
|
|
|
if Config.anonymous_participation_email_required?() &&
|
|
|
|
Config.anonymous_participation_email_confirmation_required?() do
|
|
|
|
args
|
|
|
|
|> Map.get(:email)
|
2020-06-09 10:07:30 +02:00
|
|
|
|> Email.Participation.anonymous_participation_confirmation(
|
|
|
|
participant,
|
|
|
|
Map.get(args, :locale, "en")
|
|
|
|
)
|
2021-04-20 15:02:24 +02:00
|
|
|
|> Email.Mailer.send_email_later()
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
{:ok, participant}
|
|
|
|
else
|
|
|
|
{:error, err} ->
|
|
|
|
{:error, err}
|
|
|
|
|
|
|
|
{:has_event, _} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error,
|
|
|
|
dgettext("errors", "Event with this ID %{id} doesn't exist", id: inspect(event_id))}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:anonymous_participation_enabled, false} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Anonymous participation is not enabled")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:anonymous_actor_id, false} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Profile ID provided is not the anonymous profile one")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:email_required, _} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "A valid email is required by your instance")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:actor_not_found, _} ->
|
|
|
|
Logger.error(
|
|
|
|
"The actor ID \"#{actor_id}\" provided by configuration doesn't match any actor in database"
|
|
|
|
)
|
|
|
|
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Internal Error")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def actor_join_event(_parent, _args, _resolution) do
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "You need to be logged-in to join an event")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
@spec do_actor_join_event(Actor.t(), integer | String.t(), map()) ::
|
|
|
|
{:ok, Participant.t()} | {:error, String.t()}
|
2020-02-18 08:57:00 +01:00
|
|
|
defp do_actor_join_event(actor, event_id, args) do
|
2019-12-20 13:04:34 +01:00
|
|
|
with {:has_event, {:ok, %Event{} = event}} <-
|
|
|
|
{:has_event, Events.get_event_with_preload(event_id)},
|
|
|
|
{:ok, _activity, participant} <- Participations.join(event, actor, args),
|
|
|
|
%Participant{} = participant <-
|
|
|
|
participant
|
|
|
|
|> Map.put(:event, event)
|
2020-12-15 17:17:42 +01:00
|
|
|
|> Map.put(:actor, actor) do
|
2019-12-20 13:04:34 +01:00
|
|
|
{:ok, participant}
|
|
|
|
else
|
2021-09-24 16:46:42 +02:00
|
|
|
{:error, :maximum_attendee_capacity_reached} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "The event has already reached its maximum capacity")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:has_event, _} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error,
|
|
|
|
dgettext("errors", "Event with this ID %{id} doesn't exist", id: inspect(event_id))}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:error, :event_not_found} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Event id not found")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
2021-09-24 16:46:42 +02:00
|
|
|
{:error, :already_participant} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "You are already a participant of this event")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-24 16:46:42 +02:00
|
|
|
@spec check_anonymous_participation(String.t(), String.t()) ::
|
|
|
|
{:ok, Event.t()} | {:error, String.t()}
|
|
|
|
defp check_anonymous_participation(actor_id, event_id) do
|
|
|
|
cond do
|
|
|
|
Config.anonymous_participation?() == false ->
|
|
|
|
{:error, dgettext("errors", "Anonymous participation is not enabled")}
|
|
|
|
|
|
|
|
to_string(Config.anonymous_actor_id()) != actor_id ->
|
|
|
|
{:error, dgettext("errors", "The anonymous actor ID is invalid")}
|
|
|
|
|
|
|
|
true ->
|
|
|
|
case Mobilizon.Events.get_event_with_preload(event_id) do
|
|
|
|
{:ok, %Event{} = event} ->
|
|
|
|
{:ok, event}
|
|
|
|
|
|
|
|
{:error, :event_not_found} ->
|
|
|
|
{:error,
|
|
|
|
dgettext("errors", "Event with this ID %{id} doesn't exist", id: inspect(event_id))}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-20 13:04:34 +01:00
|
|
|
@doc """
|
2020-09-30 10:45:01 +02:00
|
|
|
Leave an event for an anonymous actor
|
2019-12-20 13:04:34 +01:00
|
|
|
"""
|
2021-09-28 19:40:37 +02:00
|
|
|
@spec actor_leave_event(any(), map(), Absinthe.Resolution.t()) ::
|
|
|
|
{:ok, map()} | {:error, String.t()}
|
2019-12-20 13:04:34 +01:00
|
|
|
def actor_leave_event(
|
|
|
|
_parent,
|
|
|
|
%{actor_id: actor_id, event_id: event_id, token: token},
|
|
|
|
_resolution
|
2020-03-05 19:32:34 +01:00
|
|
|
)
|
|
|
|
when not is_nil(token) do
|
2021-09-24 16:46:42 +02:00
|
|
|
case check_anonymous_participation(actor_id, event_id) do
|
|
|
|
{:ok, %Event{} = event} ->
|
|
|
|
%Actor{} = actor = Actors.get_actor_with_preload!(actor_id)
|
|
|
|
|
|
|
|
case Participations.leave(event, actor, %{local: false, cancellation_token: token}) do
|
|
|
|
{:ok, _activity, %Participant{id: participant_id} = _participant} ->
|
|
|
|
{:ok, %{event: %{id: event_id}, actor: %{id: actor_id}, id: participant_id}}
|
|
|
|
|
|
|
|
{:error, :is_only_organizer} ->
|
|
|
|
{:error,
|
|
|
|
dgettext(
|
|
|
|
"errors",
|
|
|
|
"You can't leave event because you're the only event creator participant"
|
|
|
|
)}
|
|
|
|
|
|
|
|
{:error, :participant_not_found} ->
|
|
|
|
{:error, dgettext("errors", "Participant not found")}
|
|
|
|
|
|
|
|
{:error, _err} ->
|
|
|
|
{:error, dgettext("errors", "Failed to leave the event")}
|
|
|
|
end
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def actor_leave_event(
|
|
|
|
_parent,
|
|
|
|
%{actor_id: actor_id, event_id: event_id},
|
|
|
|
%{context: %{current_user: user}}
|
|
|
|
) do
|
|
|
|
with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
|
|
|
|
{:has_event, {:ok, %Event{} = event}} <-
|
|
|
|
{:has_event, Events.get_event_with_preload(event_id)},
|
|
|
|
{:ok, _activity, _participant} <- Participations.leave(event, actor) do
|
|
|
|
{:ok, %{event: %{id: event_id}, actor: %{id: actor_id}}}
|
|
|
|
else
|
|
|
|
{:has_event, _} ->
|
|
|
|
{:error, "Event with this ID #{inspect(event_id)} doesn't exist"}
|
|
|
|
|
|
|
|
{:is_owned, nil} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
2021-09-24 16:46:42 +02:00
|
|
|
{:error, :is_only_organizer} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error,
|
|
|
|
dgettext(
|
|
|
|
"errors",
|
|
|
|
"You can't leave event because you're the only event creator participant"
|
|
|
|
)}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:error, :participant_not_found} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Participant not found")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def actor_leave_event(_parent, _args, _resolution) do
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "You need to be logged-in to leave an event")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
|
2021-09-28 19:40:37 +02:00
|
|
|
@spec update_participation(any(), map(), Absinthe.Resolution.t()) ::
|
|
|
|
{:ok, Participation.t()} | {:error, String.t()}
|
2019-12-20 13:04:34 +01:00
|
|
|
def update_participation(
|
|
|
|
_parent,
|
2020-11-19 17:06:28 +01:00
|
|
|
%{id: participation_id, role: new_role},
|
2019-12-20 13:04:34 +01:00
|
|
|
%{
|
|
|
|
context: %{
|
2021-09-10 11:35:32 +02:00
|
|
|
current_actor: %Actor{} = moderator_actor
|
2019-12-20 13:04:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
) do
|
2021-09-10 11:35:32 +02:00
|
|
|
# Check that participation already exists
|
|
|
|
with {:has_participation, %Participant{role: old_role, event_id: event_id} = participation} <-
|
2019-12-20 13:04:34 +01:00
|
|
|
{:has_participation, Events.get_participant(participation_id)},
|
|
|
|
{:same_role, false} <- {:same_role, new_role == old_role},
|
|
|
|
# Check that moderator has right
|
2021-08-13 11:22:04 +02:00
|
|
|
{:event, %Event{} = event} <- {:event, Events.get_event_with_preload!(event_id)},
|
|
|
|
{:event_can_be_managed, true} <-
|
|
|
|
{:event_can_be_managed, can_event_be_updated_by?(event, moderator_actor)},
|
2019-12-20 13:04:34 +01:00
|
|
|
{:ok, _activity, participation} <-
|
|
|
|
Participations.update(participation, moderator_actor, new_role) do
|
|
|
|
{:ok, participation}
|
|
|
|
else
|
|
|
|
{:has_participation, nil} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Participant not found")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
2021-08-13 11:22:04 +02:00
|
|
|
{:event_can_be_managed, _} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error,
|
2021-04-28 16:13:38 +02:00
|
|
|
dgettext("errors", "Provided profile doesn't have moderator permissions on this event")}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:same_role, true} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Participant already has role %{role}", role: new_role)}
|
2019-12-20 13:04:34 +01:00
|
|
|
|
|
|
|
{:error, :participant_not_found} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "Participant not found")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@spec confirm_participation_from_token(map(), map(), map()) ::
|
|
|
|
{:ok, Participant.t()} | {:error, String.t()}
|
|
|
|
def confirm_participation_from_token(
|
|
|
|
_parent,
|
|
|
|
%{confirmation_token: confirmation_token},
|
|
|
|
_context
|
|
|
|
) do
|
|
|
|
with {:has_participant,
|
|
|
|
%Participant{actor: actor, role: :not_confirmed, event: event} = participant} <-
|
|
|
|
{:has_participant, Events.get_participant_by_confirmation_token(confirmation_token)},
|
|
|
|
default_role <- Events.get_default_participant_role(event),
|
|
|
|
{:ok, _activity, %Participant{} = participant} <-
|
|
|
|
Participations.update(participant, actor, default_role) do
|
|
|
|
{:ok, participant}
|
|
|
|
else
|
|
|
|
{:has_participant, _} ->
|
2020-09-29 09:53:48 +02:00
|
|
|
{:error, dgettext("errors", "This token is invalid")}
|
2019-12-20 13:04:34 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@spec valid_email?(String.t() | nil) :: boolean
|
|
|
|
defp valid_email?(email) when is_nil(email), do: false
|
|
|
|
|
2021-04-08 16:41:49 +02:00
|
|
|
defp valid_email?(email) when is_binary(email) do
|
2019-12-20 13:04:34 +01:00
|
|
|
email
|
|
|
|
|> String.trim()
|
|
|
|
|> Checker.valid?()
|
|
|
|
end
|
|
|
|
end
|