1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.ActivityPub do
7 alias Pleroma.Activity.Ir.Topics
9 alias Pleroma.Conversation
10 alias Pleroma.Conversation.Participation
11 alias Pleroma.Notification
13 alias Pleroma.Object.Containment
14 alias Pleroma.Object.Fetcher
15 alias Pleroma.Pagination
19 alias Pleroma.Web.ActivityPub.MRF
20 alias Pleroma.Web.ActivityPub.Transmogrifier
21 alias Pleroma.Web.ActivityPub.Utils
22 alias Pleroma.Web.Streamer
23 alias Pleroma.Web.WebFinger
24 alias Pleroma.Workers.BackgroundWorker
27 import Pleroma.Web.ActivityPub.Utils
28 import Pleroma.Web.ActivityPub.Visibility
31 require Pleroma.Constants
33 # For Announce activities, we filter the recipients based on following status for any actors
34 # that match actual users. See issue #164 for more information about why this is necessary.
35 defp get_recipients(%{"type" => "Announce"} = data) do
36 to = Map.get(data, "to", [])
37 cc = Map.get(data, "cc", [])
38 bcc = Map.get(data, "bcc", [])
39 actor = User.get_cached_by_ap_id(data["actor"])
42 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
43 case User.get_cached_by_ap_id(recipient) do
45 user -> User.following?(user, actor)
52 defp get_recipients(%{"type" => "Create"} = data) do
53 to = Map.get(data, "to", [])
54 cc = Map.get(data, "cc", [])
55 bcc = Map.get(data, "bcc", [])
56 actor = Map.get(data, "actor", [])
57 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
61 defp get_recipients(data) do
62 to = Map.get(data, "to", [])
63 cc = Map.get(data, "cc", [])
64 bcc = Map.get(data, "bcc", [])
65 recipients = Enum.concat([to, cc, bcc])
69 defp check_actor_is_active(actor) do
70 if not is_nil(actor) do
71 with user <- User.get_cached_by_ap_id(actor),
72 false <- user.deactivated do
82 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
83 limit = Config.get([:instance, :remote_limit])
84 String.length(content) <= limit
87 defp check_remote_limit(_), do: true
89 def increase_note_count_if_public(actor, object) do
90 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
93 def decrease_note_count_if_public(actor, object) do
94 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
97 def increase_replies_count_if_reply(%{
98 "object" => %{"inReplyTo" => reply_ap_id} = object,
101 if is_public?(object) do
102 Object.increase_replies_count(reply_ap_id)
106 def increase_replies_count_if_reply(_create_data), do: :noop
108 def decrease_replies_count_if_reply(%Object{
109 data: %{"inReplyTo" => reply_ap_id} = object
111 if is_public?(object) do
112 Object.decrease_replies_count(reply_ap_id)
116 def decrease_replies_count_if_reply(_object), do: :noop
118 def increase_poll_votes_if_vote(%{
119 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
122 Object.increase_vote_count(reply_ap_id, name)
125 def increase_poll_votes_if_vote(_create_data), do: :noop
127 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
128 with nil <- Activity.normalize(map),
129 map <- lazy_put_activity_defaults(map, fake),
130 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
131 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
132 {:ok, map} <- MRF.filter(map),
133 {recipients, _, _} = get_recipients(map),
134 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
135 {:containment, :ok} <- {:containment, Containment.contain_child(map)},
136 {:ok, map, object} <- insert_full_object(map) do
138 Repo.insert(%Activity{
142 recipients: recipients
145 # Splice in the child object if we have one.
147 if not is_nil(object) do
148 Map.put(activity, :object, object)
153 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
155 Notification.create_notifications(activity)
157 conversation = create_or_bump_conversation(activity, map["actor"])
158 participations = get_participations(conversation)
160 stream_out_participations(participations)
163 %Activity{} = activity ->
166 {:fake, true, map, recipients} ->
167 activity = %Activity{
171 recipients: recipients,
175 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
183 defp create_or_bump_conversation(activity, actor) do
184 with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
185 %User{} = user <- User.get_cached_by_ap_id(actor),
186 Participation.mark_as_read(user, conversation) do
191 defp get_participations({:ok, conversation}) do
193 |> Repo.preload(:participations, force: true)
194 |> Map.get(:participations)
197 defp get_participations(_), do: []
199 def stream_out_participations(participations) do
202 |> Repo.preload(:user)
204 Streamer.stream("participation", participations)
207 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
208 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
209 conversation = Repo.preload(conversation, :participations),
211 fetch_latest_activity_id_for_context(conversation.ap_id, %{
213 "blocking_user" => user
215 if last_activity_id do
216 stream_out_participations(conversation.participations)
221 def stream_out_participations(_, _), do: :noop
223 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
224 when data_type in ["Create", "Announce", "Delete"] do
226 |> Topics.get_activity_topics()
227 |> Streamer.stream(activity)
230 def stream_out(_activity) do
234 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
235 additional = params[:additional] || %{}
236 # only accept false as false value
237 local = !(params[:local] == false)
238 published = params[:published]
239 quick_insert? = Pleroma.Config.get([:env]) == :benchmark
243 %{to: to, actor: actor, published: published, context: context, object: object},
246 {:ok, activity} <- insert(create_data, local, fake),
247 {:fake, false, activity} <- {:fake, fake, activity},
248 _ <- increase_replies_count_if_reply(create_data),
249 _ <- increase_poll_votes_if_vote(create_data),
250 {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
251 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
252 :ok <- maybe_federate(activity) do
255 {:quick_insert, true, activity} ->
258 {:fake, true, activity} ->
266 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
267 additional = params[:additional] || %{}
268 # only accept false as false value
269 local = !(params[:local] == false)
270 published = params[:published]
274 %{to: to, actor: actor, published: published, context: context, object: object},
277 {:ok, activity} <- insert(listen_data, local),
278 :ok <- maybe_federate(activity) do
286 def accept(params) do
287 accept_or_reject("Accept", params)
290 def reject(params) do
291 accept_or_reject("Reject", params)
294 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
295 local = Map.get(params, :local, true)
296 activity_id = Map.get(params, :activity_id, nil)
299 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
300 |> Utils.maybe_put("id", activity_id),
301 {:ok, activity} <- insert(data, local),
302 :ok <- maybe_federate(activity) do
307 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
308 local = !(params[:local] == false)
309 activity_id = params[:activity_id]
318 data <- Utils.maybe_put(data, "id", activity_id),
319 {:ok, activity} <- insert(data, local),
320 :ok <- maybe_federate(activity) do
325 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
327 %User{ap_id: ap_id} = user,
328 %Object{data: %{"id" => _}} = object,
332 with nil <- get_existing_like(ap_id, object),
333 like_data <- make_like_data(user, object, activity_id),
334 {:ok, activity} <- insert(like_data, local),
335 {:ok, object} <- add_like_to_object(activity, object),
336 :ok <- maybe_federate(activity) do
337 {:ok, activity, object}
339 %Activity{} = activity -> {:ok, activity, object}
340 error -> {:error, error}
344 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
345 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
346 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
347 {:ok, unlike_activity} <- insert(unlike_data, local),
348 {:ok, _activity} <- Repo.delete(like_activity),
349 {:ok, object} <- remove_like_from_object(like_activity, object),
350 :ok <- maybe_federate(unlike_activity) do
351 {:ok, unlike_activity, like_activity, object}
358 %User{ap_id: _} = user,
359 %Object{data: %{"id" => _}} = object,
364 with true <- is_announceable?(object, user, public),
365 announce_data <- make_announce_data(user, object, activity_id, public),
366 {:ok, activity} <- insert(announce_data, local),
367 {:ok, object} <- add_announce_to_object(activity, object),
368 :ok <- maybe_federate(activity) do
369 {:ok, activity, object}
371 error -> {:error, error}
381 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
382 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
383 {:ok, unannounce_activity} <- insert(unannounce_data, local),
384 :ok <- maybe_federate(unannounce_activity),
385 {:ok, _activity} <- Repo.delete(announce_activity),
386 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
387 {:ok, unannounce_activity, object}
393 def follow(follower, followed, activity_id \\ nil, local \\ true) do
394 with data <- make_follow_data(follower, followed, activity_id),
395 {:ok, activity} <- insert(data, local),
396 :ok <- maybe_federate(activity),
397 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
402 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
403 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
404 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
405 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
406 {:ok, activity} <- insert(unfollow_data, local),
407 :ok <- maybe_federate(activity) do
412 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
414 "to" => [follower_address],
417 "object" => %{"type" => "Person", "id" => ap_id}
419 {:ok, activity} <- insert(data, true, true, true),
420 :ok <- maybe_federate(activity) do
425 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
426 local = Keyword.get(options, :local, true)
427 activity_id = Keyword.get(options, :activity_id, nil)
428 actor = Keyword.get(options, :actor, actor)
430 user = User.get_cached_by_ap_id(actor)
431 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
433 with {:ok, object, activity} <- Object.delete(object),
440 "deleted_activity_id" => activity && activity.id
442 |> maybe_put("id", activity_id),
443 {:ok, activity} <- insert(data, local, false),
444 stream_out_participations(object, user),
445 _ <- decrease_replies_count_if_reply(object),
446 {:ok, _actor} <- decrease_note_count_if_public(user, object),
447 :ok <- maybe_federate(activity) do
452 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
453 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
454 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
455 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
457 if unfollow_blocked do
458 follow_activity = fetch_latest_follow(blocker, blocked)
459 if follow_activity, do: unfollow(blocker, blocked, nil, local)
462 with true <- outgoing_blocks,
463 block_data <- make_block_data(blocker, blocked, activity_id),
464 {:ok, activity} <- insert(block_data, local),
465 :ok <- maybe_federate(activity) do
472 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
473 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
474 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
475 {:ok, activity} <- insert(unblock_data, local),
476 :ok <- maybe_federate(activity) do
481 @spec flag(map()) :: {:ok, Activity.t()} | any
491 # only accept false as false value
492 local = !(params[:local] == false)
493 forward = !(params[:forward] == false)
495 additional = params[:additional] || %{}
499 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
501 Map.merge(additional, %{"to" => [], "cc" => []})
504 with flag_data <- make_flag_data(params, additional),
505 {:ok, activity} <- insert(flag_data, local),
506 {:ok, stripped_activity} <- strip_report_status_data(activity),
507 :ok <- maybe_federate(stripped_activity) do
508 Enum.each(User.all_superusers(), fn superuser ->
510 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
511 |> Pleroma.Emails.Mailer.deliver_async()
518 defp fetch_activities_for_context_query(context, opts) do
519 public = [Pleroma.Constants.as_public()]
522 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
524 from(activity in Activity)
525 |> maybe_preload_objects(opts)
526 |> maybe_preload_bookmarks(opts)
527 |> maybe_set_thread_muted_field(opts)
528 |> restrict_blocked(opts)
529 |> restrict_recipients(recipients, opts["user"])
533 "?->>'type' = ? and ?->>'context' = ?",
540 |> exclude_poll_votes(opts)
542 |> order_by([activity], desc: activity.id)
545 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
546 def fetch_activities_for_context(context, opts \\ %{}) do
548 |> fetch_activities_for_context_query(opts)
552 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
553 FlakeId.Ecto.CompatType.t() | nil
554 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
556 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
562 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
563 opts = Map.drop(opts, ["user"])
565 [Pleroma.Constants.as_public()]
566 |> fetch_activities_query(opts)
567 |> restrict_unlisted()
568 |> Pagination.fetch_paginated(opts, pagination)
572 @valid_visibilities ~w[direct unlisted public private]
574 defp restrict_visibility(query, %{visibility: visibility})
575 when is_list(visibility) do
576 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
582 "activity_visibility(?, ?, ?) = ANY (?)",
592 Logger.error("Could not restrict visibility to #{visibility}")
596 defp restrict_visibility(query, %{visibility: visibility})
597 when visibility in @valid_visibilities do
601 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
605 defp restrict_visibility(_query, %{visibility: visibility})
606 when visibility not in @valid_visibilities do
607 Logger.error("Could not restrict visibility to #{visibility}")
610 defp restrict_visibility(query, _visibility), do: query
612 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
613 when is_list(visibility) do
614 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
619 "activity_visibility(?, ?, ?) = ANY (?)",
627 Logger.error("Could not exclude visibility to #{visibility}")
632 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
633 when visibility in @valid_visibilities do
638 "activity_visibility(?, ?, ?) = ?",
647 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
648 when visibility not in @valid_visibilities do
649 Logger.error("Could not exclude visibility to #{visibility}")
653 defp exclude_visibility(query, _visibility), do: query
655 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
658 defp restrict_thread_visibility(
660 %{"user" => %User{skip_thread_containment: true}},
665 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
668 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
672 defp restrict_thread_visibility(query, _, _), do: query
674 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
677 |> Map.put("user", reading_user)
678 |> Map.put("actor_id", user.ap_id)
679 |> Map.put("whole_db", true)
682 user_activities_recipients(%{
683 "godmode" => params["godmode"],
684 "reading_user" => reading_user
687 fetch_activities(recipients, params)
691 def fetch_user_activities(user, reading_user, params \\ %{}) do
694 |> Map.put("type", ["Create", "Announce"])
695 |> Map.put("user", reading_user)
696 |> Map.put("actor_id", user.ap_id)
697 |> Map.put("whole_db", true)
698 |> Map.put("pinned_activity_ids", user.pinned_activities)
701 user_activities_recipients(%{
702 "godmode" => params["godmode"],
703 "reading_user" => reading_user
706 fetch_activities(recipients, params)
710 defp user_activities_recipients(%{"godmode" => true}) do
714 defp user_activities_recipients(%{"reading_user" => reading_user}) do
716 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
718 [Pleroma.Constants.as_public()]
722 defp restrict_since(query, %{"since_id" => ""}), do: query
724 defp restrict_since(query, %{"since_id" => since_id}) do
725 from(activity in query, where: activity.id > ^since_id)
728 defp restrict_since(query, _), do: query
730 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
731 raise "Can't use the child object without preloading!"
734 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
735 when is_list(tag_reject) and tag_reject != [] do
737 [_activity, object] in query,
738 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
742 defp restrict_tag_reject(query, _), do: query
744 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
745 raise "Can't use the child object without preloading!"
748 defp restrict_tag_all(query, %{"tag_all" => tag_all})
749 when is_list(tag_all) and tag_all != [] do
751 [_activity, object] in query,
752 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
756 defp restrict_tag_all(query, _), do: query
758 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
759 raise "Can't use the child object without preloading!"
762 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
764 [_activity, object] in query,
765 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
769 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
771 [_activity, object] in query,
772 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
776 defp restrict_tag(query, _), do: query
778 defp restrict_recipients(query, [], _user), do: query
780 defp restrict_recipients(query, recipients, nil) do
781 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
784 defp restrict_recipients(query, recipients, user) do
787 where: fragment("? && ?", ^recipients, activity.recipients),
788 or_where: activity.actor == ^user.ap_id
792 defp restrict_local(query, %{"local_only" => true}) do
793 from(activity in query, where: activity.local == true)
796 defp restrict_local(query, _), do: query
798 defp restrict_actor(query, %{"actor_id" => actor_id}) do
799 from(activity in query, where: activity.actor == ^actor_id)
802 defp restrict_actor(query, _), do: query
804 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
805 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
808 defp restrict_type(query, %{"type" => type}) do
809 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
812 defp restrict_type(query, _), do: query
814 defp restrict_state(query, %{"state" => state}) do
815 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
818 defp restrict_state(query, _), do: query
820 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
822 [_activity, object] in query,
823 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
827 defp restrict_favorited_by(query, _), do: query
829 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
830 raise "Can't use the child object without preloading!"
833 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
835 [_activity, object] in query,
836 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
840 defp restrict_media(query, _), do: query
842 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
844 [_activity, object] in query,
845 where: fragment("?->>'inReplyTo' is null", object.data)
849 defp restrict_replies(query, _), do: query
851 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
852 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
855 defp restrict_reblogs(query, _), do: query
857 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
859 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
863 from([activity] in query,
864 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
865 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
868 unless opts["skip_preload"] do
869 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
875 defp restrict_muted(query, _), do: query
877 defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
878 blocks = user.blocks || []
879 domain_blocks = user.domain_blocks || []
882 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
885 [activity, object: o] in query,
886 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
887 where: fragment("not (? && ?)", activity.recipients, ^blocks),
890 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
895 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
896 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
900 defp restrict_blocked(query, _), do: query
902 defp restrict_unlisted(query) do
907 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
909 ^[Pleroma.Constants.as_public()]
914 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
915 from(activity in query, where: activity.id in ^ids)
918 defp restrict_pinned(query, _), do: query
920 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
921 muted_reblogs = user.muted_reblogs || []
927 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
935 defp restrict_muted_reblogs(query, _), do: query
937 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
939 defp exclude_poll_votes(query, _) do
940 if has_named_binding?(query, :object) do
941 from([activity, object: o] in query,
942 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
949 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
950 from(activity in query, where: activity.id != ^id)
953 defp exclude_id(query, _), do: query
955 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
957 defp maybe_preload_objects(query, _) do
959 |> Activity.with_preloaded_object()
962 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
964 defp maybe_preload_bookmarks(query, opts) do
966 |> Activity.with_preloaded_bookmark(opts["user"])
969 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
971 defp maybe_set_thread_muted_field(query, opts) do
973 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
976 defp maybe_order(query, %{order: :desc}) do
978 |> order_by(desc: :id)
981 defp maybe_order(query, %{order: :asc}) do
983 |> order_by(asc: :id)
986 defp maybe_order(query, _), do: query
988 def fetch_activities_query(recipients, opts \\ %{}) do
990 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
994 |> maybe_preload_objects(opts)
995 |> maybe_preload_bookmarks(opts)
996 |> maybe_set_thread_muted_field(opts)
998 |> restrict_recipients(recipients, opts["user"])
999 |> restrict_tag(opts)
1000 |> restrict_tag_reject(opts)
1001 |> restrict_tag_all(opts)
1002 |> restrict_since(opts)
1003 |> restrict_local(opts)
1004 |> restrict_actor(opts)
1005 |> restrict_type(opts)
1006 |> restrict_state(opts)
1007 |> restrict_favorited_by(opts)
1008 |> restrict_blocked(opts)
1009 |> restrict_muted(opts)
1010 |> restrict_media(opts)
1011 |> restrict_visibility(opts)
1012 |> restrict_thread_visibility(opts, config)
1013 |> restrict_replies(opts)
1014 |> restrict_reblogs(opts)
1015 |> restrict_pinned(opts)
1016 |> restrict_muted_reblogs(opts)
1017 |> Activity.restrict_deactivated_users()
1018 |> exclude_poll_votes(opts)
1019 |> exclude_visibility(opts)
1022 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1023 list_memberships = Pleroma.List.memberships(opts["user"])
1025 fetch_activities_query(recipients ++ list_memberships, opts)
1026 |> Pagination.fetch_paginated(opts, pagination)
1028 |> maybe_update_cc(list_memberships, opts["user"])
1031 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1032 when is_list(list_memberships) and length(list_memberships) > 0 do
1033 Enum.map(activities, fn
1034 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1035 if Enum.any?(bcc, &(&1 in list_memberships)) do
1036 update_in(activity.data["cc"], &[user_ap_id | &1])
1046 defp maybe_update_cc(activities, _, _), do: activities
1048 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1049 from(activity in query,
1051 fragment("? && ?", activity.recipients, ^recipients) or
1052 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1053 ^Pleroma.Constants.as_public() in activity.recipients)
1057 def fetch_activities_bounded(
1059 recipients_with_public,
1061 pagination \\ :keyset
1063 fetch_activities_query([], opts)
1064 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1065 |> Pagination.fetch_paginated(opts, pagination)
1069 def upload(file, opts \\ []) do
1070 with {:ok, data} <- Upload.store(file, opts) do
1073 Map.put(data, "actor", opts[:actor])
1078 Repo.insert(%Object{data: obj_data})
1082 defp object_to_user_data(data) do
1084 data["icon"]["url"] &&
1087 "url" => [%{"href" => data["icon"]["url"]}]
1091 data["image"]["url"] &&
1094 "url" => [%{"href" => data["image"]["url"]}]
1099 |> Map.get("attachment", [])
1100 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1101 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1103 locked = data["manuallyApprovesFollowers"] || false
1104 data = Transmogrifier.maybe_fix_user_object(data)
1105 discoverable = data["discoverable"] || false
1106 invisible = data["invisible"] || false
1115 discoverable: discoverable,
1116 invisible: invisible,
1119 follower_address: data["followers"],
1120 following_address: data["following"],
1121 bio: data["summary"]
1124 # nickname can be nil because of virtual actors
1126 if data["preferredUsername"] do
1130 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1133 Map.put(user_data, :nickname, nil)
1139 def fetch_follow_information_for_user(user) do
1140 with {:ok, following_data} <-
1141 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1142 following_count when is_integer(following_count) <- following_data["totalItems"],
1143 {:ok, hide_follows} <- collection_private(following_data),
1144 {:ok, followers_data} <-
1145 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1146 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1147 {:ok, hide_followers} <- collection_private(followers_data) do
1150 hide_follows: hide_follows,
1151 follower_count: followers_count,
1152 following_count: following_count,
1153 hide_followers: hide_followers
1164 defp maybe_update_follow_information(data) do
1165 with {:enabled, true} <-
1166 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1167 {:ok, info} <- fetch_follow_information_for_user(data) do
1168 info = Map.merge(data[:info] || %{}, info)
1169 Map.put(data, :info, info)
1171 {:enabled, false} ->
1176 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1183 defp collection_private(data) do
1184 if is_map(data["first"]) and
1185 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1188 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1189 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1192 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1204 def user_data_from_user_object(data) do
1205 with {:ok, data} <- MRF.filter(data),
1206 {:ok, data} <- object_to_user_data(data) do
1213 def fetch_and_prepare_user_from_ap_id(ap_id) do
1214 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1215 {:ok, data} <- user_data_from_user_object(data),
1216 data <- maybe_update_follow_information(data) do
1220 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1225 def make_user_from_ap_id(ap_id) do
1226 if _user = User.get_cached_by_ap_id(ap_id) do
1227 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1229 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1230 User.insert_or_update_user(data)
1237 def make_user_from_nickname(nickname) do
1238 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1239 make_user_from_ap_id(ap_id)
1241 _e -> {:error, "No AP id in WebFinger"}
1245 # filter out broken threads
1246 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1247 entire_thread_visible_for_user?(activity, user)
1250 # do post-processing on a specific activity
1251 def contain_activity(%Activity{} = activity, %User{} = user) do
1252 contain_broken_threads(activity, user)
1255 def fetch_direct_messages_query do
1257 |> restrict_type(%{"type" => "Create"})
1258 |> restrict_visibility(%{visibility: "direct"})
1259 |> order_by([activity], asc: activity.id)