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()]
523 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
526 from(activity in Activity)
527 |> maybe_preload_objects(opts)
528 |> maybe_preload_bookmarks(opts)
529 |> maybe_set_thread_muted_field(opts)
530 |> restrict_blocked(opts)
531 |> restrict_recipients(recipients, opts["user"])
535 "?->>'type' = ? and ?->>'context' = ?",
542 |> exclude_poll_votes(opts)
544 |> order_by([activity], desc: activity.id)
547 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
548 def fetch_activities_for_context(context, opts \\ %{}) do
550 |> fetch_activities_for_context_query(opts)
554 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
555 FlakeId.Ecto.CompatType.t() | nil
556 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
558 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
564 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
565 opts = Map.drop(opts, ["user"])
567 [Pleroma.Constants.as_public()]
568 |> fetch_activities_query(opts)
569 |> restrict_unlisted()
570 |> Pagination.fetch_paginated(opts, pagination)
573 @valid_visibilities ~w[direct unlisted public private]
575 defp restrict_visibility(query, %{visibility: visibility})
576 when is_list(visibility) do
577 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
583 "activity_visibility(?, ?, ?) = ANY (?)",
593 Logger.error("Could not restrict visibility to #{visibility}")
597 defp restrict_visibility(query, %{visibility: visibility})
598 when visibility in @valid_visibilities do
602 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
606 defp restrict_visibility(_query, %{visibility: visibility})
607 when visibility not in @valid_visibilities do
608 Logger.error("Could not restrict visibility to #{visibility}")
611 defp restrict_visibility(query, _visibility), do: query
613 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
614 when is_list(visibility) do
615 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
620 "activity_visibility(?, ?, ?) = ANY (?)",
628 Logger.error("Could not exclude visibility to #{visibility}")
633 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
634 when visibility in @valid_visibilities do
639 "activity_visibility(?, ?, ?) = ?",
648 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
649 when visibility not in @valid_visibilities do
650 Logger.error("Could not exclude visibility to #{visibility}")
654 defp exclude_visibility(query, _visibility), do: query
656 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
659 defp restrict_thread_visibility(
661 %{"user" => %User{skip_thread_containment: true}},
666 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
669 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
673 defp restrict_thread_visibility(query, _, _), do: query
675 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
678 |> Map.put("user", reading_user)
679 |> Map.put("actor_id", user.ap_id)
680 |> Map.put("whole_db", true)
683 user_activities_recipients(%{
684 "godmode" => params["godmode"],
685 "reading_user" => reading_user
688 fetch_activities(recipients, params)
692 def fetch_user_activities(user, reading_user, params \\ %{}) do
695 |> Map.put("type", ["Create", "Announce"])
696 |> Map.put("user", reading_user)
697 |> Map.put("actor_id", user.ap_id)
698 |> Map.put("whole_db", true)
699 |> Map.put("pinned_activity_ids", user.pinned_activities)
702 user_activities_recipients(%{
703 "godmode" => params["godmode"],
704 "reading_user" => reading_user
707 fetch_activities(recipients, params)
711 defp user_activities_recipients(%{"godmode" => true}) do
715 defp user_activities_recipients(%{"reading_user" => reading_user}) do
717 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
719 [Pleroma.Constants.as_public()]
723 defp restrict_since(query, %{"since_id" => ""}), do: query
725 defp restrict_since(query, %{"since_id" => since_id}) do
726 from(activity in query, where: activity.id > ^since_id)
729 defp restrict_since(query, _), do: query
731 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
732 raise "Can't use the child object without preloading!"
735 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
736 when is_list(tag_reject) and tag_reject != [] do
738 [_activity, object] in query,
739 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
743 defp restrict_tag_reject(query, _), do: query
745 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
746 raise "Can't use the child object without preloading!"
749 defp restrict_tag_all(query, %{"tag_all" => tag_all})
750 when is_list(tag_all) and tag_all != [] do
752 [_activity, object] in query,
753 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
757 defp restrict_tag_all(query, _), do: query
759 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
760 raise "Can't use the child object without preloading!"
763 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
765 [_activity, object] in query,
766 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
770 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
772 [_activity, object] in query,
773 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
777 defp restrict_tag(query, _), do: query
779 defp restrict_recipients(query, [], _user), do: query
781 defp restrict_recipients(query, recipients, nil) do
782 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
785 defp restrict_recipients(query, recipients, user) do
788 where: fragment("? && ?", ^recipients, activity.recipients),
789 or_where: activity.actor == ^user.ap_id
793 defp restrict_local(query, %{"local_only" => true}) do
794 from(activity in query, where: activity.local == true)
797 defp restrict_local(query, _), do: query
799 defp restrict_actor(query, %{"actor_id" => actor_id}) do
800 from(activity in query, where: activity.actor == ^actor_id)
803 defp restrict_actor(query, _), do: query
805 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
806 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
809 defp restrict_type(query, %{"type" => type}) do
810 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
813 defp restrict_type(query, _), do: query
815 defp restrict_state(query, %{"state" => state}) do
816 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
819 defp restrict_state(query, _), do: query
821 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
823 [_activity, object] in query,
824 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
828 defp restrict_favorited_by(query, _), do: query
830 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
831 raise "Can't use the child object without preloading!"
834 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
836 [_activity, object] in query,
837 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
841 defp restrict_media(query, _), do: query
843 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
845 [_activity, object] in query,
846 where: fragment("?->>'inReplyTo' is null", object.data)
850 defp restrict_replies(query, _), do: query
852 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
853 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
856 defp restrict_reblogs(query, _), do: query
858 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
860 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
864 from([activity] in query,
865 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
866 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
869 unless opts["skip_preload"] do
870 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
876 defp restrict_muted(query, _), do: query
878 defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
879 blocks = user.blocks || []
880 domain_blocks = user.domain_blocks || []
883 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
886 [activity, object: o] in query,
887 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
888 where: fragment("not (? && ?)", activity.recipients, ^blocks),
891 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
896 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
897 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
901 defp restrict_blocked(query, _), do: query
903 defp restrict_unlisted(query) do
908 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
910 ^[Pleroma.Constants.as_public()]
915 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
916 from(activity in query, where: activity.id in ^ids)
919 defp restrict_pinned(query, _), do: query
921 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
922 muted_reblogs = user.muted_reblogs || []
928 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
936 defp restrict_muted_reblogs(query, _), do: query
938 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
940 defp exclude_poll_votes(query, _) do
941 if has_named_binding?(query, :object) do
942 from([activity, object: o] in query,
943 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
950 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
951 from(activity in query, where: activity.id != ^id)
954 defp exclude_id(query, _), do: query
956 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
958 defp maybe_preload_objects(query, _) do
960 |> Activity.with_preloaded_object()
963 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
965 defp maybe_preload_bookmarks(query, opts) do
967 |> Activity.with_preloaded_bookmark(opts["user"])
970 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
972 defp maybe_set_thread_muted_field(query, opts) do
974 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
977 defp maybe_order(query, %{order: :desc}) do
979 |> order_by(desc: :id)
982 defp maybe_order(query, %{order: :asc}) do
984 |> order_by(asc: :id)
987 defp maybe_order(query, _), do: query
989 def fetch_activities_query(recipients, opts \\ %{}) do
991 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
995 |> maybe_preload_objects(opts)
996 |> maybe_preload_bookmarks(opts)
997 |> maybe_set_thread_muted_field(opts)
999 |> restrict_recipients(recipients, opts["user"])
1000 |> restrict_tag(opts)
1001 |> restrict_tag_reject(opts)
1002 |> restrict_tag_all(opts)
1003 |> restrict_since(opts)
1004 |> restrict_local(opts)
1005 |> restrict_actor(opts)
1006 |> restrict_type(opts)
1007 |> restrict_state(opts)
1008 |> restrict_favorited_by(opts)
1009 |> restrict_blocked(opts)
1010 |> restrict_muted(opts)
1011 |> restrict_media(opts)
1012 |> restrict_visibility(opts)
1013 |> restrict_thread_visibility(opts, config)
1014 |> restrict_replies(opts)
1015 |> restrict_reblogs(opts)
1016 |> restrict_pinned(opts)
1017 |> restrict_muted_reblogs(opts)
1018 |> Activity.restrict_deactivated_users()
1019 |> exclude_poll_votes(opts)
1020 |> exclude_visibility(opts)
1023 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1024 list_memberships = Pleroma.List.memberships(opts["user"])
1026 fetch_activities_query(recipients ++ list_memberships, opts)
1027 |> Pagination.fetch_paginated(opts, pagination)
1029 |> maybe_update_cc(list_memberships, opts["user"])
1032 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1033 when is_list(list_memberships) and length(list_memberships) > 0 do
1034 Enum.map(activities, fn
1035 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1036 if Enum.any?(bcc, &(&1 in list_memberships)) do
1037 update_in(activity.data["cc"], &[user_ap_id | &1])
1047 defp maybe_update_cc(activities, _, _), do: activities
1049 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1050 from(activity in query,
1052 fragment("? && ?", activity.recipients, ^recipients) or
1053 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1054 ^Pleroma.Constants.as_public() in activity.recipients)
1058 def fetch_activities_bounded(
1060 recipients_with_public,
1062 pagination \\ :keyset
1064 fetch_activities_query([], opts)
1065 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1066 |> Pagination.fetch_paginated(opts, pagination)
1070 def upload(file, opts \\ []) do
1071 with {:ok, data} <- Upload.store(file, opts) do
1074 Map.put(data, "actor", opts[:actor])
1079 Repo.insert(%Object{data: obj_data})
1083 defp object_to_user_data(data) do
1085 data["icon"]["url"] &&
1088 "url" => [%{"href" => data["icon"]["url"]}]
1092 data["image"]["url"] &&
1095 "url" => [%{"href" => data["image"]["url"]}]
1100 |> Map.get("attachment", [])
1101 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1102 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1104 locked = data["manuallyApprovesFollowers"] || false
1105 data = Transmogrifier.maybe_fix_user_object(data)
1106 discoverable = data["discoverable"] || false
1107 invisible = data["invisible"] || false
1116 discoverable: discoverable,
1117 invisible: invisible,
1120 follower_address: data["followers"],
1121 following_address: data["following"],
1122 bio: data["summary"]
1125 # nickname can be nil because of virtual actors
1127 if data["preferredUsername"] do
1131 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1134 Map.put(user_data, :nickname, nil)
1140 def fetch_follow_information_for_user(user) do
1141 with {:ok, following_data} <-
1142 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1143 following_count when is_integer(following_count) <- following_data["totalItems"],
1144 {:ok, hide_follows} <- collection_private(following_data),
1145 {:ok, followers_data} <-
1146 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1147 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1148 {:ok, hide_followers} <- collection_private(followers_data) do
1151 hide_follows: hide_follows,
1152 follower_count: followers_count,
1153 following_count: following_count,
1154 hide_followers: hide_followers
1165 defp maybe_update_follow_information(data) do
1166 with {:enabled, true} <-
1167 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1168 {:ok, info} <- fetch_follow_information_for_user(data) do
1169 info = Map.merge(data[:info] || %{}, info)
1170 Map.put(data, :info, info)
1172 {:enabled, false} ->
1177 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1184 defp collection_private(data) do
1185 if is_map(data["first"]) and
1186 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1189 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1190 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1193 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1205 def user_data_from_user_object(data) do
1206 with {:ok, data} <- MRF.filter(data),
1207 {:ok, data} <- object_to_user_data(data) do
1214 def fetch_and_prepare_user_from_ap_id(ap_id) do
1215 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1216 {:ok, data} <- user_data_from_user_object(data),
1217 data <- maybe_update_follow_information(data) do
1221 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1226 def make_user_from_ap_id(ap_id) do
1227 if _user = User.get_cached_by_ap_id(ap_id) do
1228 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1230 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1231 User.insert_or_update_user(data)
1238 def make_user_from_nickname(nickname) do
1239 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1240 make_user_from_ap_id(ap_id)
1242 _e -> {:error, "No AP id in WebFinger"}
1246 # filter out broken threads
1247 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1248 entire_thread_visible_for_user?(activity, user)
1251 # do post-processing on a specific activity
1252 def contain_activity(%Activity{} = activity, %User{} = user) do
1253 contain_broken_threads(activity, user)
1256 def fetch_direct_messages_query do
1258 |> restrict_type(%{"type" => "Create"})
1259 |> restrict_visibility(%{visibility: "direct"})
1260 |> order_by([activity], asc: activity.id)