From 9845b3a82d3ddfb084a2e2c416ae15c1d6899e86 Mon Sep 17 00:00:00 2001 From: Laurent GAY Date: Tue, 5 Aug 2025 16:16:26 +0200 Subject: [PATCH] atom/ics link copy to clipboard: correct URL copied for notificationsview + correct events list (participated + owned) (Issuer #1496) --- lib/service/export/common.ex | 20 ++++- package-lock.json | 2 +- src/views/Settings/NotificationsView.vue | 12 +-- test/service/export/feed_test.exs | 96 ++++++++++++++++++++++++ test/service/export/icalendar_test.exs | 93 ++++++++++++++++++++++- 5 files changed, 209 insertions(+), 14 deletions(-) diff --git a/lib/service/export/common.ex b/lib/service/export/common.ex index 6ffc96987..8068da4ae 100644 --- a/lib/service/export/common.ex +++ b/lib/service/export/common.ex @@ -16,10 +16,10 @@ defmodule Mobilizon.Service.Export.Common do def fetch_actor_event_feed(name, limit) do case Actors.get_actor_by_name(name) do %Actor{} = actor -> + %Page{elements: posts} = Posts.get_public_posts_for_group(actor, 1, limit) + if Actor.public_visibility?(actor) do - %Page{elements: events} = Events.list_public_upcoming_events_for_actor(actor, 1, limit) - %Page{elements: posts} = Posts.get_public_posts_for_group(actor, 1, limit) - {:ok, actor, events, posts} + {:ok, actor, fetch_events_from_actor(actor, limit), posts} else {:error, :actor_not_public} end @@ -78,7 +78,7 @@ defmodule Mobilizon.Service.Export.Common do %{ type: :actor, actor: actor, - events: fetch_actor_private_events(actor, limit), + events: fetch_events_from_actor(actor, limit), user: user, token: token } @@ -94,6 +94,18 @@ defmodule Mobilizon.Service.Export.Common do end end + @spec fetch_actor_private_events(Actor.t(), integer()) :: list(Event.t()) + defp fetch_events_from_actor(%Actor{} = actor, limit) do + events_participate = fetch_actor_private_events(actor, limit) + %Page{elements: events_owner} = Events.list_public_upcoming_events_for_actor(actor, 1, limit) + event_participate_ids = events_participate |> Enum.map(fn other -> other.id end) + + events_owner = + events_owner |> Enum.filter(fn event -> event.id not in event_participate_ids end) + + events_participate ++ events_owner + end + @spec fetch_instance_public_content(integer()) :: {:ok, list(Event.t()), list(Post.t())} def fetch_instance_public_content(limit) do %Page{elements: events} = Events.list_public_local_events(1, limit, :begins_on, :desc) diff --git a/package-lock.json b/package-lock.json index 735861cfb..7541d44e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "mobilizon", - "version": "5.1.4", + "version": "5.1.5", "hasInstallScript": true, "dependencies": { "@apollo/client": "^3.13.8", diff --git a/src/views/Settings/NotificationsView.vue b/src/views/Settings/NotificationsView.vue index a64388dfa..2703181b7 100644 --- a/src/views/Settings/NotificationsView.vue +++ b/src/views/Settings/NotificationsView.vue @@ -250,7 +250,7 @@ (e: Event) => copyURL( e, - tokenToURL('events/going/' + feedToken.token + 'atom'), + tokenToURL('events/going/' + feedToken.token + '/atom'), 'atom' ) " @@ -258,11 +258,11 @@ (e: Event) => copyURL( e, - tokenToURL('events/going/' + feedToken.token + 'atom'), + tokenToURL('events/going/' + feedToken.token + '/atom'), 'atom' ) " - :href="tokenToURL('events/going/' + feedToken.token + 'atom')" + :href="tokenToURL('events/going/' + feedToken.token + '/atom')" target="_blank" >{{ $t("RSS/Atom Feed") }} @@ -279,7 +279,7 @@ (e: Event) => copyURL( e, - tokenToURL('events/going/' + feedToken.token + 'ics'), + tokenToURL('events/going/' + feedToken.token + '/ics'), 'ics' ) " @@ -287,12 +287,12 @@ (e: Event) => copyURL( e, - tokenToURL('events/going/' + feedToken.token + 'ics'), + tokenToURL('events/going/' + feedToken.token + '/ics'), 'ics' ) " icon-left="calendar-sync" - :href="tokenToURL('events/going/' + feedToken.token + 'ics')" + :href="tokenToURL('events/going/' + feedToken.token + '/ics')" target="_blank" >{{ $t("ICS/WebCal Feed") }} diff --git a/test/service/export/feed_test.exs b/test/service/export/feed_test.exs index 8cb199584..70527e859 100644 --- a/test/service/export/feed_test.exs +++ b/test/service/export/feed_test.exs @@ -91,5 +91,101 @@ defmodule Mobilizon.Service.Feed do rss = Regex.replace(~r|\.*\|, rss, "") assert rss == String.replace(expectedrss, "\n", "") end + + test "an actor feedtoken simple" do + user = insert(:user) + actor = insert(:actor, user: user) + %FeedToken{token: token} = insert(:feed_token, user: user, actor: actor) + + event1 = insert(:event, title: "event owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = insert(:event, visibility: :private, title: "I'm private") + event4 = insert(:event, title: "No description", description: nil) + insert(:participant, event: event2, actor: actor, role: :participant) + + {:commit, ics} = FeedService.create_cache("token_#{ShortUUID.encode!(token)}") + refute ics =~ event4.title + refute ics =~ event3.title + assert ics =~ event1.title + assert ics =~ event2.title + end + + test "by actor preferred_username simple" do + user = insert(:user) + actor = insert(:actor, user: user) + + event1 = insert(:event, title: "event owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = insert(:event, visibility: :private, title: "I'm private") + event4 = insert(:event, title: "No description", description: nil) + insert(:participant, event: event2, actor: actor, role: :participant) + + {:commit, ics} = FeedService.create_cache("actor_#{actor.preferred_username}") + refute ics =~ event4.title + refute ics =~ event3.title + assert ics =~ event1.title + assert ics =~ event2.title + end + + test "by actor feedtoken complexe" do + user = insert(:user) + actor = insert(:actor, user: user) + %FeedToken{token: token} = insert(:feed_token, user: user, actor: actor) + + event1 = + insert(:event, title: "event simple owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = + insert(:event, + title: "event owner and particiated", + description: "owner & particiated", + picture: nil, + organizer_actor: actor + ) + + insert(:participant, event: event2, actor: actor, role: :participant) + insert(:participant, event: event3, actor: actor, role: :participant) + + {:commit, ics} = FeedService.create_cache("token_#{ShortUUID.encode!(token)}") + assert ics |> String.split(event1.title) |> length() == 2 + assert ics |> String.split(event2.title) |> length() == 2 + assert ics |> String.split(event3.title) |> length() == 2 + end + + test "by actor preferred_username complexe" do + user = insert(:user) + actor = insert(:actor, user: user) + + event1 = + insert(:event, title: "event simple owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = + insert(:event, + title: "event owner and particiated", + description: "owner & particiated", + picture: nil, + organizer_actor: actor + ) + + insert(:participant, event: event2, actor: actor, role: :participant) + insert(:participant, event: event3, actor: actor, role: :participant) + + {:commit, ics} = FeedService.create_cache("actor_#{actor.preferred_username}") + assert ics |> String.split(event1.title) |> length() == 2 + assert ics |> String.split(event2.title) |> length() == 2 + assert ics |> String.split(event3.title) |> length() == 2 + end end end diff --git a/test/service/export/icalendar_test.exs b/test/service/export/icalendar_test.exs index 0e17ea787..b7ea4712d 100644 --- a/test/service/export/icalendar_test.exs +++ b/test/service/export/icalendar_test.exs @@ -82,11 +82,98 @@ defmodule Mobilizon.Service.ICalendarTest do user = insert(:user) actor = insert(:actor, user: user) %FeedToken{token: token} = insert(:feed_token, user: user, actor: actor) - event = insert(:event) - insert(:participant, event: event, actor: actor, role: :participant) + + event1 = insert(:event, title: "event owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = insert(:event, visibility: :private, title: "I'm private") + event4 = insert(:event, title: "No description", description: nil) + + insert(:participant, event: event2, actor: actor, role: :participant) {:commit, ics} = ICalendarService.create_cache("token_#{ShortUUID.encode!(token)}") - assert ics =~ event.title + refute ics =~ event4.title + refute ics =~ event3.title + assert ics =~ event2.title + assert ics =~ event1.title + end + + test "by actor preferred_username" do + user = insert(:user) + actor = insert(:actor, user: user) + + event1 = insert(:event, title: "event owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = insert(:event, visibility: :private, title: "I'm private") + event4 = insert(:event, title: "No description", description: nil) + + insert(:participant, event: event2, actor: actor, role: :participant) + + {:commit, ics} = ICalendarService.create_cache("actor_#{actor.preferred_username}") + refute ics =~ event4.title + refute ics =~ event3.title + assert ics =~ event1.title + assert ics =~ event2.title + end + + test "by actor feedtoken complexe" do + user = insert(:user) + actor = insert(:actor, user: user) + %FeedToken{token: token} = insert(:feed_token, user: user, actor: actor) + + event1 = + insert(:event, title: "event simple owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = + insert(:event, + title: "event owner and particiated", + description: "owner & particiated", + picture: nil, + organizer_actor: actor + ) + + insert(:participant, event: event2, actor: actor, role: :participant) + insert(:participant, event: event3, actor: actor, role: :participant) + + {:commit, ics} = ICalendarService.create_cache("token_#{ShortUUID.encode!(token)}") + assert ics |> String.split(event1.title) |> length() == 2 + assert ics |> String.split(event2.title) |> length() == 2 + assert ics |> String.split(event3.title) |> length() == 2 + end + + test "by actor preferred_username complexe" do + user = insert(:user) + actor = insert(:actor, user: user) + + event1 = + insert(:event, title: "event simple owner", description: "owner", organizer_actor: actor) + + event2 = + insert(:event, title: "event particiated", description: "particiated", picture: nil) + + event3 = + insert(:event, + title: "event owner and particiated", + description: "owner & particiated", + picture: nil, + organizer_actor: actor + ) + + insert(:participant, event: event2, actor: actor, role: :participant) + insert(:participant, event: event3, actor: actor, role: :participant) + + {:commit, ics} = ICalendarService.create_cache("actor_#{actor.preferred_username}") + assert ics |> String.split(event1.title) |> length() == 2 + assert ics |> String.split(event2.title) |> length() == 2 + assert ics |> String.split(event3.title) |> length() == 2 end end end