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.info.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 # Changing note count prior to enqueuing federation task in order to avoid
252 # race conditions on updating user.info
253 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
254 :ok <- maybe_federate(activity) do
257 {:quick_insert, true, activity} ->
260 {:fake, true, activity} ->
268 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
269 additional = params[:additional] || %{}
270 # only accept false as false value
271 local = !(params[:local] == false)
272 published = params[:published]
276 %{to: to, actor: actor, published: published, context: context, object: object},
279 {:ok, activity} <- insert(listen_data, local),
280 :ok <- maybe_federate(activity) do
288 def accept(params) do
289 accept_or_reject("Accept", params)
292 def reject(params) do
293 accept_or_reject("Reject", params)
296 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
297 local = Map.get(params, :local, true)
298 activity_id = Map.get(params, :activity_id, nil)
301 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
302 |> Utils.maybe_put("id", activity_id),
303 {:ok, activity} <- insert(data, local),
304 :ok <- maybe_federate(activity) do
309 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
310 local = !(params[:local] == false)
311 activity_id = params[:activity_id]
320 data <- Utils.maybe_put(data, "id", activity_id),
321 {:ok, activity} <- insert(data, local),
322 :ok <- maybe_federate(activity) do
327 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
329 %User{ap_id: ap_id} = user,
330 %Object{data: %{"id" => _}} = object,
334 with nil <- get_existing_like(ap_id, object),
335 like_data <- make_like_data(user, object, activity_id),
336 {:ok, activity} <- insert(like_data, local),
337 {:ok, object} <- add_like_to_object(activity, object),
338 :ok <- maybe_federate(activity) do
339 {:ok, activity, object}
341 %Activity{} = activity -> {:ok, activity, object}
342 error -> {:error, error}
346 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
347 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
348 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
349 {:ok, unlike_activity} <- insert(unlike_data, local),
350 {:ok, _activity} <- Repo.delete(like_activity),
351 {:ok, object} <- remove_like_from_object(like_activity, object),
352 :ok <- maybe_federate(unlike_activity) do
353 {:ok, unlike_activity, like_activity, object}
360 %User{ap_id: _} = user,
361 %Object{data: %{"id" => _}} = object,
366 with true <- is_announceable?(object, user, public),
367 announce_data <- make_announce_data(user, object, activity_id, public),
368 {:ok, activity} <- insert(announce_data, local),
369 {:ok, object} <- add_announce_to_object(activity, object),
370 :ok <- maybe_federate(activity) do
371 {:ok, activity, object}
373 error -> {:error, error}
383 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
384 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
385 {:ok, unannounce_activity} <- insert(unannounce_data, local),
386 :ok <- maybe_federate(unannounce_activity),
387 {:ok, _activity} <- Repo.delete(announce_activity),
388 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
389 {:ok, unannounce_activity, object}
395 def follow(follower, followed, activity_id \\ nil, local \\ true) do
396 with data <- make_follow_data(follower, followed, activity_id),
397 {:ok, activity} <- insert(data, local),
398 :ok <- maybe_federate(activity),
399 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
404 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
405 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
406 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
407 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
408 {:ok, activity} <- insert(unfollow_data, local),
409 :ok <- maybe_federate(activity) do
414 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
416 "to" => [follower_address],
419 "object" => %{"type" => "Person", "id" => ap_id}
421 {:ok, activity} <- insert(data, true, true, true),
422 :ok <- maybe_federate(activity) do
427 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
428 local = Keyword.get(options, :local, true)
429 activity_id = Keyword.get(options, :activity_id, nil)
430 actor = Keyword.get(options, :actor, actor)
432 user = User.get_cached_by_ap_id(actor)
433 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
435 with {:ok, object, activity} <- Object.delete(object),
442 "deleted_activity_id" => activity && activity.id
444 |> maybe_put("id", activity_id),
445 {:ok, activity} <- insert(data, local, false),
446 stream_out_participations(object, user),
447 _ <- decrease_replies_count_if_reply(object),
448 # Changing note count prior to enqueuing federation task in order to avoid
449 # race conditions on updating user.info
450 {:ok, _actor} <- decrease_note_count_if_public(user, object),
451 :ok <- maybe_federate(activity) do
456 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
457 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
458 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
459 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
461 if unfollow_blocked do
462 follow_activity = fetch_latest_follow(blocker, blocked)
463 if follow_activity, do: unfollow(blocker, blocked, nil, local)
466 with true <- outgoing_blocks,
467 block_data <- make_block_data(blocker, blocked, activity_id),
468 {:ok, activity} <- insert(block_data, local),
469 :ok <- maybe_federate(activity) do
476 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
477 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
478 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
479 {:ok, activity} <- insert(unblock_data, local),
480 :ok <- maybe_federate(activity) do
485 @spec flag(map()) :: {:ok, Activity.t()} | any
495 # only accept false as false value
496 local = !(params[:local] == false)
497 forward = !(params[:forward] == false)
499 additional = params[:additional] || %{}
503 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
505 Map.merge(additional, %{"to" => [], "cc" => []})
508 with flag_data <- make_flag_data(params, additional),
509 {:ok, activity} <- insert(flag_data, local),
510 :ok <- maybe_federate(activity) do
511 Enum.each(User.all_superusers(), fn superuser ->
513 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
514 |> Pleroma.Emails.Mailer.deliver_async()
521 defp fetch_activities_for_context_query(context, opts) do
522 public = [Pleroma.Constants.as_public()]
525 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
527 from(activity in Activity)
528 |> maybe_preload_objects(opts)
529 |> maybe_preload_bookmarks(opts)
530 |> maybe_set_thread_muted_field(opts)
531 |> restrict_blocked(opts)
532 |> restrict_recipients(recipients, opts["user"])
536 "?->>'type' = ? and ?->>'context' = ?",
543 |> exclude_poll_votes(opts)
545 |> order_by([activity], desc: activity.id)
548 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
549 def fetch_activities_for_context(context, opts \\ %{}) do
551 |> fetch_activities_for_context_query(opts)
555 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
556 FlakeId.Ecto.CompatType.t() | nil
557 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
559 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
565 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
566 opts = Map.drop(opts, ["user"])
568 [Pleroma.Constants.as_public()]
569 |> fetch_activities_query(opts)
570 |> restrict_unlisted()
571 |> Pagination.fetch_paginated(opts, pagination)
575 @valid_visibilities ~w[direct unlisted public private]
577 defp restrict_visibility(query, %{visibility: visibility})
578 when is_list(visibility) do
579 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
585 "activity_visibility(?, ?, ?) = ANY (?)",
595 Logger.error("Could not restrict visibility to #{visibility}")
599 defp restrict_visibility(query, %{visibility: visibility})
600 when visibility in @valid_visibilities do
604 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
608 defp restrict_visibility(_query, %{visibility: visibility})
609 when visibility not in @valid_visibilities do
610 Logger.error("Could not restrict visibility to #{visibility}")
613 defp restrict_visibility(query, _visibility), do: query
615 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
616 when is_list(visibility) do
617 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
622 "activity_visibility(?, ?, ?) = ANY (?)",
630 Logger.error("Could not exclude visibility to #{visibility}")
635 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
636 when visibility in @valid_visibilities do
641 "activity_visibility(?, ?, ?) = ?",
650 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
651 when visibility not in @valid_visibilities do
652 Logger.error("Could not exclude visibility to #{visibility}")
656 defp exclude_visibility(query, _visibility), do: query
658 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
661 defp restrict_thread_visibility(
663 %{"user" => %User{info: %{skip_thread_containment: true}}},
668 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
671 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
675 defp restrict_thread_visibility(query, _, _), do: query
677 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
680 |> Map.put("user", reading_user)
681 |> Map.put("actor_id", user.ap_id)
682 |> Map.put("whole_db", true)
685 user_activities_recipients(%{
686 "godmode" => params["godmode"],
687 "reading_user" => reading_user
690 fetch_activities(recipients, params)
694 def fetch_user_activities(user, reading_user, params \\ %{}) do
697 |> Map.put("type", ["Create", "Announce"])
698 |> Map.put("user", reading_user)
699 |> Map.put("actor_id", user.ap_id)
700 |> Map.put("whole_db", true)
701 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
704 user_activities_recipients(%{
705 "godmode" => params["godmode"],
706 "reading_user" => reading_user
709 fetch_activities(recipients, params)
713 defp user_activities_recipients(%{"godmode" => true}) do
717 defp user_activities_recipients(%{"reading_user" => reading_user}) do
719 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
721 [Pleroma.Constants.as_public()]
725 defp restrict_since(query, %{"since_id" => ""}), do: query
727 defp restrict_since(query, %{"since_id" => since_id}) do
728 from(activity in query, where: activity.id > ^since_id)
731 defp restrict_since(query, _), do: query
733 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
734 raise "Can't use the child object without preloading!"
737 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
738 when is_list(tag_reject) and tag_reject != [] do
740 [_activity, object] in query,
741 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
745 defp restrict_tag_reject(query, _), do: query
747 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
748 raise "Can't use the child object without preloading!"
751 defp restrict_tag_all(query, %{"tag_all" => tag_all})
752 when is_list(tag_all) and tag_all != [] do
754 [_activity, object] in query,
755 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
759 defp restrict_tag_all(query, _), do: query
761 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
762 raise "Can't use the child object without preloading!"
765 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
767 [_activity, object] in query,
768 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
772 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
774 [_activity, object] in query,
775 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
779 defp restrict_tag(query, _), do: query
781 defp restrict_recipients(query, [], _user), do: query
783 defp restrict_recipients(query, recipients, nil) do
784 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
787 defp restrict_recipients(query, recipients, user) do
790 where: fragment("? && ?", ^recipients, activity.recipients),
791 or_where: activity.actor == ^user.ap_id
795 defp restrict_local(query, %{"local_only" => true}) do
796 from(activity in query, where: activity.local == true)
799 defp restrict_local(query, _), do: query
801 defp restrict_actor(query, %{"actor_id" => actor_id}) do
802 from(activity in query, where: activity.actor == ^actor_id)
805 defp restrict_actor(query, _), do: query
807 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
808 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
811 defp restrict_type(query, %{"type" => type}) do
812 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
815 defp restrict_type(query, _), do: query
817 defp restrict_state(query, %{"state" => state}) do
818 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
821 defp restrict_state(query, _), do: query
823 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
825 [_activity, object] in query,
826 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
830 defp restrict_favorited_by(query, _), do: query
832 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
833 raise "Can't use the child object without preloading!"
836 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
838 [_activity, object] in query,
839 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
843 defp restrict_media(query, _), do: query
845 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
847 [_activity, object] in query,
848 where: fragment("?->>'inReplyTo' is null", object.data)
852 defp restrict_replies(query, _), do: query
854 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
855 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
858 defp restrict_reblogs(query, _), do: query
860 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
862 defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
866 from([activity] in query,
867 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
868 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
871 unless opts["skip_preload"] do
872 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
878 defp restrict_muted(query, _), do: query
880 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
881 blocks = info.blocks || []
882 domain_blocks = info.domain_blocks || []
885 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
888 [activity, object: o] in query,
889 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
890 where: fragment("not (? && ?)", activity.recipients, ^blocks),
893 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
898 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
899 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
903 defp restrict_blocked(query, _), do: query
905 defp restrict_unlisted(query) do
910 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
912 ^[Pleroma.Constants.as_public()]
917 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
918 from(activity in query, where: activity.id in ^ids)
921 defp restrict_pinned(query, _), do: query
923 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
924 muted_reblogs = info.muted_reblogs || []
930 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
938 defp restrict_muted_reblogs(query, _), do: query
940 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
942 defp exclude_poll_votes(query, _) do
943 if has_named_binding?(query, :object) do
944 from([activity, object: o] in query,
945 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
952 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
953 from(activity in query, where: activity.id != ^id)
956 defp exclude_id(query, _), do: query
958 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
960 defp maybe_preload_objects(query, _) do
962 |> Activity.with_preloaded_object()
965 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
967 defp maybe_preload_bookmarks(query, opts) do
969 |> Activity.with_preloaded_bookmark(opts["user"])
972 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
974 defp maybe_set_thread_muted_field(query, opts) do
976 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
979 defp maybe_order(query, %{order: :desc}) do
981 |> order_by(desc: :id)
984 defp maybe_order(query, %{order: :asc}) do
986 |> order_by(asc: :id)
989 defp maybe_order(query, _), do: query
991 def fetch_activities_query(recipients, opts \\ %{}) do
993 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
997 |> maybe_preload_objects(opts)
998 |> maybe_preload_bookmarks(opts)
999 |> maybe_set_thread_muted_field(opts)
1000 |> maybe_order(opts)
1001 |> restrict_recipients(recipients, opts["user"])
1002 |> restrict_tag(opts)
1003 |> restrict_tag_reject(opts)
1004 |> restrict_tag_all(opts)
1005 |> restrict_since(opts)
1006 |> restrict_local(opts)
1007 |> restrict_actor(opts)
1008 |> restrict_type(opts)
1009 |> restrict_state(opts)
1010 |> restrict_favorited_by(opts)
1011 |> restrict_blocked(opts)
1012 |> restrict_muted(opts)
1013 |> restrict_media(opts)
1014 |> restrict_visibility(opts)
1015 |> restrict_thread_visibility(opts, config)
1016 |> restrict_replies(opts)
1017 |> restrict_reblogs(opts)
1018 |> restrict_pinned(opts)
1019 |> restrict_muted_reblogs(opts)
1020 |> Activity.restrict_deactivated_users()
1021 |> exclude_poll_votes(opts)
1022 |> exclude_visibility(opts)
1025 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1026 list_memberships = Pleroma.List.memberships(opts["user"])
1028 fetch_activities_query(recipients ++ list_memberships, opts)
1029 |> Pagination.fetch_paginated(opts, pagination)
1031 |> maybe_update_cc(list_memberships, opts["user"])
1034 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1035 when is_list(list_memberships) and length(list_memberships) > 0 do
1036 Enum.map(activities, fn
1037 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1038 if Enum.any?(bcc, &(&1 in list_memberships)) do
1039 update_in(activity.data["cc"], &[user_ap_id | &1])
1049 defp maybe_update_cc(activities, _, _), do: activities
1051 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1052 from(activity in query,
1054 fragment("? && ?", activity.recipients, ^recipients) or
1055 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1056 ^Pleroma.Constants.as_public() in activity.recipients)
1060 def fetch_activities_bounded(
1062 recipients_with_public,
1064 pagination \\ :keyset
1066 fetch_activities_query([], opts)
1067 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1068 |> Pagination.fetch_paginated(opts, pagination)
1072 def upload(file, opts \\ []) do
1073 with {:ok, data} <- Upload.store(file, opts) do
1076 Map.put(data, "actor", opts[:actor])
1081 Repo.insert(%Object{data: obj_data})
1085 defp object_to_user_data(data) do
1087 data["icon"]["url"] &&
1090 "url" => [%{"href" => data["icon"]["url"]}]
1094 data["image"]["url"] &&
1097 "url" => [%{"href" => data["image"]["url"]}]
1102 |> Map.get("attachment", [])
1103 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1104 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1106 locked = data["manuallyApprovesFollowers"] || false
1107 data = Transmogrifier.maybe_fix_user_object(data)
1108 discoverable = data["discoverable"] || false
1109 invisible = data["invisible"] || false
1119 discoverable: discoverable,
1120 invisible: invisible
1124 follower_address: data["followers"],
1125 following_address: data["following"],
1126 bio: data["summary"]
1129 # nickname can be nil because of virtual actors
1131 if data["preferredUsername"] do
1135 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1138 Map.put(user_data, :nickname, nil)
1144 def fetch_follow_information_for_user(user) do
1145 with {:ok, following_data} <-
1146 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1147 following_count when is_integer(following_count) <- following_data["totalItems"],
1148 {:ok, hide_follows} <- collection_private(following_data),
1149 {:ok, followers_data} <-
1150 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1151 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1152 {:ok, hide_followers} <- collection_private(followers_data) do
1155 hide_follows: hide_follows,
1156 follower_count: followers_count,
1157 following_count: following_count,
1158 hide_followers: hide_followers
1169 defp maybe_update_follow_information(data) do
1170 with {:enabled, true} <-
1171 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1172 {:ok, info} <- fetch_follow_information_for_user(data) do
1173 info = Map.merge(data.info, info)
1174 Map.put(data, :info, info)
1176 {:enabled, false} ->
1181 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1188 defp collection_private(data) do
1189 if is_map(data["first"]) and
1190 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1193 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1194 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1197 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1209 def user_data_from_user_object(data) do
1210 with {:ok, data} <- MRF.filter(data),
1211 {:ok, data} <- object_to_user_data(data) do
1218 def fetch_and_prepare_user_from_ap_id(ap_id) do
1219 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1220 {:ok, data} <- user_data_from_user_object(data),
1221 data <- maybe_update_follow_information(data) do
1225 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1230 def make_user_from_ap_id(ap_id) do
1231 if _user = User.get_cached_by_ap_id(ap_id) do
1232 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1234 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1235 User.insert_or_update_user(data)
1242 def make_user_from_nickname(nickname) do
1243 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1244 make_user_from_ap_id(ap_id)
1246 _e -> {:error, "No AP id in WebFinger"}
1250 # filter out broken threads
1251 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1252 entire_thread_visible_for_user?(activity, user)
1255 # do post-processing on a specific activity
1256 def contain_activity(%Activity{} = activity, %User{} = user) do
1257 contain_broken_threads(activity, user)
1260 def fetch_direct_messages_query do
1262 |> restrict_type(%{"type" => "Create"})
1263 |> restrict_visibility(%{visibility: "direct"})
1264 |> order_by([activity], asc: activity.id)