From 9e6b232a785fdbdcd2f183ab69f5c13e91d752df Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 1 Dec 2023 09:52:28 +0100 Subject: [PATCH] feat: add links to cancel anonymous participations in emails Signed-off-by: Thomas Citharel --- lib/graphql/resolvers/participant.ex | 11 +- lib/graphql/schema/events/participant.ex | 4 +- lib/web/controllers/page_controller.ex | 2 + lib/web/email/participation.ex | 2 + lib/web/router.ex | 6 + ...ymous_participation_confirmation.html.heex | 33 +++++- .../event_participation_confirmed.html.heex | 69 ++++++++--- .../Participation/CancelParticipation.vue | 107 ++++++++++++++++++ src/graphql/event.ts | 5 + src/i18n/en_US.json | 8 +- src/i18n/fr_FR.json | 8 +- src/router/event.ts | 13 +++ 12 files changed, 244 insertions(+), 24 deletions(-) create mode 100644 src/components/Participation/CancelParticipation.vue diff --git a/lib/graphql/resolvers/participant.ex b/lib/graphql/resolvers/participant.ex index ad5058c1..6b3efa5d 100644 --- a/lib/graphql/resolvers/participant.ex +++ b/lib/graphql/resolvers/participant.ex @@ -176,7 +176,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do 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}} + {:ok, %Event{} = event} = Events.get_event_with_preload(event_id) + %Actor{} = actor = Actors.get_actor_with_preload!(actor_id) + {:ok, %{event: event, actor: actor, id: participant_id}} {:error, :is_only_organizer} -> {:error, @@ -202,8 +204,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant 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}}} + {:ok, _activity, %Participant{id: participant_id} = _participant} <- + Participations.leave(event, actor) do + {:ok, %Event{} = event} = Events.get_event_with_preload(event_id) + %Actor{} = actor = Actors.get_actor_with_preload!(actor_id) + {:ok, %{event: event, actor: actor, id: participant_id}} else {:has_event, _} -> {:error, "Event with this ID #{inspect(event_id)} doesn't exist"} diff --git a/lib/graphql/schema/events/participant.ex b/lib/graphql/schema/events/participant.ex index 0585173a..07ba90e8 100644 --- a/lib/graphql/schema/events/participant.ex +++ b/lib/graphql/schema/events/participant.ex @@ -90,8 +90,8 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do object :deleted_participant do meta(:authorize, :all) field(:id, :id, description: "The participant ID") - field(:event, :deleted_object, description: "The participant's event") - field(:actor, :deleted_object, description: "The participant's actor") + field(:event, :event, description: "The participant's event") + field(:actor, :actor, description: "The participant's actor") end object :participant_mutations do diff --git a/lib/web/controllers/page_controller.ex b/lib/web/controllers/page_controller.ex index dcb80f39..104097c2 100644 --- a/lib/web/controllers/page_controller.ex +++ b/lib/web/controllers/page_controller.ex @@ -26,6 +26,8 @@ defmodule Mobilizon.Web.PageController do defdelegate moderation_report(conn, params), to: PageController, as: :index @spec participation_email_confirmation(Plug.Conn.t(), any) :: Plug.Conn.t() defdelegate participation_email_confirmation(conn, params), to: PageController, as: :index + @spec participation_email_cancellation(Plug.Conn.t(), any) :: Plug.Conn.t() + defdelegate participation_email_cancellation(conn, params), to: PageController, as: :index @spec user_email_validation(Plug.Conn.t(), any) :: Plug.Conn.t() defdelegate user_email_validation(conn, params), to: PageController, as: :index @spec my_groups(Plug.Conn.t(), any) :: Plug.Conn.t() diff --git a/lib/web/email/participation.ex b/lib/web/email/participation.ex index 9a101d10..6ac606bb 100644 --- a/lib/web/email/participation.ex +++ b/lib/web/email/participation.ex @@ -99,6 +99,7 @@ defmodule Mobilizon.Web.Email.Participation do locale: locale, event: event, jsonLDMetadata: json_ld(participant), + participant: participant, subject: subject }) end @@ -123,6 +124,7 @@ defmodule Mobilizon.Web.Email.Participation do locale: locale, event: event, jsonLDMetadata: json_ld(participant), + participant: participant, subject: subject }) end diff --git a/lib/web/router.ex b/lib/web/router.ex index c290d268..9a1534e8 100644 --- a/lib/web/router.ex +++ b/lib/web/router.ex @@ -195,6 +195,12 @@ defmodule Mobilizon.Web.Router do get("/participation/email/confirm/:token", PageController, :participation_email_confirmation) + get( + "/participation/email/cancel/:uuid/:token", + PageController, + :participation_email_cancellation + ) + get("/validate/email/:token", PageController, :user_email_validation) get("/groups/me", PageController, :my_groups) diff --git a/lib/web/templates/email/anonymous_participation_confirmation.html.heex b/lib/web/templates/email/anonymous_participation_confirmation.html.heex index fa37af8f..50f4f9e8 100644 --- a/lib/web/templates/email/anonymous_participation_confirmation.html.heex +++ b/lib/web/templates/email/anonymous_participation_confirmation.html.heex @@ -87,14 +87,39 @@ style="padding: 20px 30px 40px 30px; color: #474467; font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >

- <%= ngettext( - "Would you wish to cancel your attendance, visit the event page through the link above and click the « Attending » button.", - "Would you wish to cancel your attendance to one or several events, visit the event pages through the links above and click the « Attending » button.", - 1 + <%= gettext( + "If you wish to cancel your participation, simply click on the link below." ) %>

+ + + + + + +
+ + + + +
+ url() + |> URI.decode() + } + target="_blank" + style="font-size: 20px; font-family: 'Roboto', Helvetica, Arial, sans-serif; color: #3C376E; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #3C376E; display: inline-block;" + > + <%= gettext("Cancel my attendance") %> + +
+
+ + - - -

- <%= gettext( - "Would you wish to update or cancel your attendance, simply access the event page through the link above and click on the Attending button." - ) %> -

- - + <%= if @participant.metadata.cancellation_token do %> + + +

+ <%= gettext( + "If you wish to cancel your participation, simply click on the link below." + ) %> +

+ + + + + + + + +
+ + + + +
+ url() + |> URI.decode() + } + target="_blank" + style="font-size: 20px; font-family: 'Roboto', Helvetica, Arial, sans-serif; color: #3C376E; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #3C376E; display: inline-block;" + > + <%= gettext("Cancel my attendance") %> + +
+
+ + + <% else %> + + +

+ <%= gettext( + "Would you wish to update or cancel your attendance, simply access the event page through the link above and click on the Attending button." + ) %> +

+ + + <% end %>