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.Notification
12 alias Pleroma.Object.Containment
13 alias Pleroma.Object.Fetcher
14 alias Pleroma.Pagination
18 alias Pleroma.Web.ActivityPub.MRF
19 alias Pleroma.Web.ActivityPub.Transmogrifier
20 alias Pleroma.Web.ActivityPub.Utils
21 alias Pleroma.Web.Streamer
22 alias Pleroma.Web.WebFinger
23 alias Pleroma.Workers.BackgroundWorker
26 import Pleroma.Web.ActivityPub.Utils
27 import Pleroma.Web.ActivityPub.Visibility
30 require Pleroma.Constants
32 # For Announce activities, we filter the recipients based on following status for any actors
33 # that match actual users. See issue #164 for more information about why this is necessary.
34 defp get_recipients(%{"type" => "Announce"} = data) do
35 to = Map.get(data, "to", [])
36 cc = Map.get(data, "cc", [])
37 bcc = Map.get(data, "bcc", [])
38 actor = User.get_cached_by_ap_id(data["actor"])
41 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
42 case User.get_cached_by_ap_id(recipient) do
44 user -> User.following?(user, actor)
51 defp get_recipients(%{"type" => "Create"} = data) do
52 to = Map.get(data, "to", [])
53 cc = Map.get(data, "cc", [])
54 bcc = Map.get(data, "bcc", [])
55 actor = Map.get(data, "actor", [])
56 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
60 defp get_recipients(data) do
61 to = Map.get(data, "to", [])
62 cc = Map.get(data, "cc", [])
63 bcc = Map.get(data, "bcc", [])
64 recipients = Enum.concat([to, cc, bcc])
68 defp check_actor_is_active(actor) do
69 if not is_nil(actor) do
70 with user <- User.get_cached_by_ap_id(actor),
71 false <- user.info.deactivated do
81 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
82 limit = Config.get([:instance, :remote_limit])
83 String.length(content) <= limit
86 defp check_remote_limit(_), do: true
88 def increase_note_count_if_public(actor, object) do
89 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
92 def decrease_note_count_if_public(actor, object) do
93 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
96 def increase_replies_count_if_reply(%{
97 "object" => %{"inReplyTo" => reply_ap_id} = object,
100 if is_public?(object) do
101 Object.increase_replies_count(reply_ap_id)
105 def increase_replies_count_if_reply(_create_data), do: :noop
107 def decrease_replies_count_if_reply(%Object{
108 data: %{"inReplyTo" => reply_ap_id} = object
110 if is_public?(object) do
111 Object.decrease_replies_count(reply_ap_id)
115 def decrease_replies_count_if_reply(_object), do: :noop
117 def increase_poll_votes_if_vote(%{
118 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
121 Object.increase_vote_count(reply_ap_id, name)
124 def increase_poll_votes_if_vote(_create_data), do: :noop
126 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
127 with nil <- Activity.normalize(map),
128 map <- lazy_put_activity_defaults(map, fake),
129 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
130 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
131 {:ok, map} <- MRF.filter(map),
132 {recipients, _, _} = get_recipients(map),
133 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
134 :ok <- Containment.contain_child(map),
135 {:ok, map, object} <- insert_full_object(map) do
137 Repo.insert(%Activity{
141 recipients: recipients
144 # Splice in the child object if we have one.
146 if not is_nil(object) do
147 Map.put(activity, :object, object)
152 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
154 Notification.create_notifications(activity)
158 |> Conversation.create_or_bump_for()
159 |> get_participations()
162 stream_out_participations(participations)
165 %Activity{} = activity ->
168 {:fake, true, map, recipients} ->
169 activity = %Activity{
173 recipients: recipients,
177 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
185 defp get_participations({:ok, %{participations: participations}}), do: participations
186 defp get_participations(_), do: []
188 def stream_out_participations(participations) do
191 |> Repo.preload(:user)
193 Streamer.stream("participation", participations)
196 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
197 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
198 conversation = Repo.preload(conversation, :participations),
200 fetch_latest_activity_id_for_context(conversation.ap_id, %{
202 "blocking_user" => user
204 if last_activity_id do
205 stream_out_participations(conversation.participations)
210 def stream_out_participations(_, _), do: :noop
212 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
213 when data_type in ["Create", "Announce", "Delete"] do
215 |> Topics.get_activity_topics()
216 |> Streamer.stream(activity)
219 def stream_out(_activity) do
223 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
224 additional = params[:additional] || %{}
225 # only accept false as false value
226 local = !(params[:local] == false)
227 published = params[:published]
231 %{to: to, actor: actor, published: published, context: context, object: object},
234 {:ok, activity} <- insert(create_data, local, fake),
235 {:fake, false, activity} <- {:fake, fake, activity},
236 _ <- increase_replies_count_if_reply(create_data),
237 _ <- increase_poll_votes_if_vote(create_data),
238 # Changing note count prior to enqueuing federation task in order to avoid
239 # race conditions on updating user.info
240 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
241 :ok <- maybe_federate(activity) do
244 {:fake, true, activity} ->
252 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
253 additional = params[:additional] || %{}
254 # only accept false as false value
255 local = !(params[:local] == false)
256 published = params[:published]
260 %{to: to, actor: actor, published: published, context: context, object: object},
263 {:ok, activity} <- insert(listen_data, local),
264 :ok <- maybe_federate(activity) do
272 def accept(params) do
273 accept_or_reject("Accept", params)
276 def reject(params) do
277 accept_or_reject("Reject", params)
280 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
281 local = Map.get(params, :local, true)
282 activity_id = Map.get(params, :activity_id, nil)
285 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
286 |> Utils.maybe_put("id", activity_id),
287 {:ok, activity} <- insert(data, local),
288 :ok <- maybe_federate(activity) do
293 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
294 local = !(params[:local] == false)
295 activity_id = params[:activity_id]
304 data <- Utils.maybe_put(data, "id", activity_id),
305 {:ok, activity} <- insert(data, local),
306 :ok <- maybe_federate(activity) do
311 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
313 %User{ap_id: ap_id} = user,
314 %Object{data: %{"id" => _}} = object,
318 with nil <- get_existing_like(ap_id, object),
319 like_data <- make_like_data(user, object, activity_id),
320 {:ok, activity} <- insert(like_data, local),
321 {:ok, object} <- add_like_to_object(activity, object),
322 :ok <- maybe_federate(activity) do
323 {:ok, activity, object}
325 %Activity{} = activity -> {:ok, activity, object}
326 error -> {:error, error}
330 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
331 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
332 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
333 {:ok, unlike_activity} <- insert(unlike_data, local),
334 {:ok, _activity} <- Repo.delete(like_activity),
335 {:ok, object} <- remove_like_from_object(like_activity, object),
336 :ok <- maybe_federate(unlike_activity) do
337 {:ok, unlike_activity, like_activity, object}
344 %User{ap_id: _} = user,
345 %Object{data: %{"id" => _}} = object,
350 with true <- is_announceable?(object, user, public),
351 announce_data <- make_announce_data(user, object, activity_id, public),
352 {:ok, activity} <- insert(announce_data, local),
353 {:ok, object} <- add_announce_to_object(activity, object),
354 :ok <- maybe_federate(activity) do
355 {:ok, activity, object}
357 error -> {:error, error}
367 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
368 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
369 {:ok, unannounce_activity} <- insert(unannounce_data, local),
370 :ok <- maybe_federate(unannounce_activity),
371 {:ok, _activity} <- Repo.delete(announce_activity),
372 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
373 {:ok, unannounce_activity, object}
379 def follow(follower, followed, activity_id \\ nil, local \\ true) do
380 with data <- make_follow_data(follower, followed, activity_id),
381 {:ok, activity} <- insert(data, local),
382 :ok <- maybe_federate(activity),
383 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
388 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
389 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
390 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
391 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
392 {:ok, activity} <- insert(unfollow_data, local),
393 :ok <- maybe_federate(activity) do
398 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
400 "to" => [follower_address],
403 "object" => %{"type" => "Person", "id" => ap_id}
405 {:ok, activity} <- insert(data, true, true, true),
406 :ok <- maybe_federate(activity) do
411 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
412 local = Keyword.get(options, :local, true)
413 activity_id = Keyword.get(options, :activity_id, nil)
414 actor = Keyword.get(options, :actor, actor)
416 user = User.get_cached_by_ap_id(actor)
417 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
419 with {:ok, object, activity} <- Object.delete(object),
426 "deleted_activity_id" => activity && activity.id
428 |> maybe_put("id", activity_id),
429 {:ok, activity} <- insert(data, local, false),
430 stream_out_participations(object, user),
431 _ <- decrease_replies_count_if_reply(object),
432 # Changing note count prior to enqueuing federation task in order to avoid
433 # race conditions on updating user.info
434 {:ok, _actor} <- decrease_note_count_if_public(user, object),
435 :ok <- maybe_federate(activity) do
440 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
441 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
442 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
443 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
445 if unfollow_blocked do
446 follow_activity = fetch_latest_follow(blocker, blocked)
447 if follow_activity, do: unfollow(blocker, blocked, nil, local)
450 with true <- outgoing_blocks,
451 block_data <- make_block_data(blocker, blocked, activity_id),
452 {:ok, activity} <- insert(block_data, local),
453 :ok <- maybe_federate(activity) do
460 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
461 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
462 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
463 {:ok, activity} <- insert(unblock_data, local),
464 :ok <- maybe_federate(activity) do
469 @spec flag(map()) :: {:ok, Activity.t()} | any
479 # only accept false as false value
480 local = !(params[:local] == false)
481 forward = !(params[:forward] == false)
483 additional = params[:additional] || %{}
487 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
489 Map.merge(additional, %{"to" => [], "cc" => []})
492 with flag_data <- make_flag_data(params, additional),
493 {:ok, activity} <- insert(flag_data, local),
494 {:ok, stripped_activity} <- strip_report_status_data(activity),
495 :ok <- maybe_federate(stripped_activity) do
496 Enum.each(User.all_superusers(), fn superuser ->
498 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
499 |> Pleroma.Emails.Mailer.deliver_async()
506 defp fetch_activities_for_context_query(context, opts) do
507 public = [Pleroma.Constants.as_public()]
510 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
512 from(activity in Activity)
513 |> maybe_preload_objects(opts)
514 |> maybe_preload_bookmarks(opts)
515 |> maybe_set_thread_muted_field(opts)
516 |> restrict_blocked(opts)
517 |> restrict_recipients(recipients, opts["user"])
521 "?->>'type' = ? and ?->>'context' = ?",
528 |> exclude_poll_votes(opts)
530 |> order_by([activity], desc: activity.id)
533 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
534 def fetch_activities_for_context(context, opts \\ %{}) do
536 |> fetch_activities_for_context_query(opts)
540 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
541 FlakeId.Ecto.CompatType.t() | nil
542 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
544 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
550 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
551 opts = Map.drop(opts, ["user"])
553 [Pleroma.Constants.as_public()]
554 |> fetch_activities_query(opts)
555 |> restrict_unlisted()
556 |> Pagination.fetch_paginated(opts, pagination)
560 @valid_visibilities ~w[direct unlisted public private]
562 defp restrict_visibility(query, %{visibility: visibility})
563 when is_list(visibility) do
564 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
570 "activity_visibility(?, ?, ?) = ANY (?)",
580 Logger.error("Could not restrict visibility to #{visibility}")
584 defp restrict_visibility(query, %{visibility: visibility})
585 when visibility in @valid_visibilities do
589 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
593 defp restrict_visibility(_query, %{visibility: visibility})
594 when visibility not in @valid_visibilities do
595 Logger.error("Could not restrict visibility to #{visibility}")
598 defp restrict_visibility(query, _visibility), do: query
600 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
601 when is_list(visibility) do
602 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
607 "activity_visibility(?, ?, ?) = ANY (?)",
615 Logger.error("Could not exclude visibility to #{visibility}")
620 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
621 when visibility in @valid_visibilities do
626 "activity_visibility(?, ?, ?) = ?",
635 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
636 when visibility not in @valid_visibilities do
637 Logger.error("Could not exclude visibility to #{visibility}")
641 defp exclude_visibility(query, _visibility), do: query
643 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
646 defp restrict_thread_visibility(
648 %{"user" => %User{info: %{skip_thread_containment: true}}},
653 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
656 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
660 defp restrict_thread_visibility(query, _, _), do: query
662 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
665 |> Map.put("user", reading_user)
666 |> Map.put("actor_id", user.ap_id)
667 |> Map.put("whole_db", true)
670 user_activities_recipients(%{
671 "godmode" => params["godmode"],
672 "reading_user" => reading_user
675 fetch_activities(recipients, params)
679 def fetch_user_activities(user, reading_user, params \\ %{}) do
682 |> Map.put("type", ["Create", "Announce"])
683 |> Map.put("user", reading_user)
684 |> Map.put("actor_id", user.ap_id)
685 |> Map.put("whole_db", true)
686 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
689 user_activities_recipients(%{
690 "godmode" => params["godmode"],
691 "reading_user" => reading_user
694 fetch_activities(recipients, params)
698 defp user_activities_recipients(%{"godmode" => true}) do
702 defp user_activities_recipients(%{"reading_user" => reading_user}) do
704 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
706 [Pleroma.Constants.as_public()]
710 defp restrict_since(query, %{"since_id" => ""}), do: query
712 defp restrict_since(query, %{"since_id" => since_id}) do
713 from(activity in query, where: activity.id > ^since_id)
716 defp restrict_since(query, _), do: query
718 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
719 raise "Can't use the child object without preloading!"
722 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
723 when is_list(tag_reject) and tag_reject != [] do
725 [_activity, object] in query,
726 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
730 defp restrict_tag_reject(query, _), do: query
732 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
733 raise "Can't use the child object without preloading!"
736 defp restrict_tag_all(query, %{"tag_all" => tag_all})
737 when is_list(tag_all) and tag_all != [] do
739 [_activity, object] in query,
740 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
744 defp restrict_tag_all(query, _), do: query
746 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
747 raise "Can't use the child object without preloading!"
750 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
752 [_activity, object] in query,
753 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
757 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
759 [_activity, object] in query,
760 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
764 defp restrict_tag(query, _), do: query
766 defp restrict_recipients(query, [], _user), do: query
768 defp restrict_recipients(query, recipients, nil) do
769 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
772 defp restrict_recipients(query, recipients, user) do
775 where: fragment("? && ?", ^recipients, activity.recipients),
776 or_where: activity.actor == ^user.ap_id
780 defp restrict_local(query, %{"local_only" => true}) do
781 from(activity in query, where: activity.local == true)
784 defp restrict_local(query, _), do: query
786 defp restrict_actor(query, %{"actor_id" => actor_id}) do
787 from(activity in query, where: activity.actor == ^actor_id)
790 defp restrict_actor(query, _), do: query
792 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
793 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
796 defp restrict_type(query, %{"type" => type}) do
797 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
800 defp restrict_type(query, _), do: query
802 defp restrict_state(query, %{"state" => state}) do
803 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
806 defp restrict_state(query, _), do: query
808 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
810 [_activity, object] in query,
811 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
815 defp restrict_favorited_by(query, _), do: query
817 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
818 raise "Can't use the child object without preloading!"
821 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
823 [_activity, object] in query,
824 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
828 defp restrict_media(query, _), do: query
830 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
832 [_activity, object] in query,
833 where: fragment("?->>'inReplyTo' is null", object.data)
837 defp restrict_replies(query, _), do: query
839 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
840 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
843 defp restrict_reblogs(query, _), do: query
845 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
847 defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
851 from([activity] in query,
852 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
853 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
856 unless opts["skip_preload"] do
857 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
863 defp restrict_muted(query, _), do: query
865 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
866 blocks = info.blocks || []
867 domain_blocks = info.domain_blocks || []
870 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
873 [activity, object: o] in query,
874 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
875 where: fragment("not (? && ?)", activity.recipients, ^blocks),
878 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
883 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
884 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
888 defp restrict_blocked(query, _), do: query
890 defp restrict_unlisted(query) do
895 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
897 ^[Pleroma.Constants.as_public()]
902 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
903 from(activity in query, where: activity.id in ^ids)
906 defp restrict_pinned(query, _), do: query
908 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
909 muted_reblogs = info.muted_reblogs || []
915 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
923 defp restrict_muted_reblogs(query, _), do: query
925 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
927 defp exclude_poll_votes(query, _) do
928 if has_named_binding?(query, :object) do
929 from([activity, object: o] in query,
930 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
937 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
938 from(activity in query, where: activity.id != ^id)
941 defp exclude_id(query, _), do: query
943 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
945 defp maybe_preload_objects(query, _) do
947 |> Activity.with_preloaded_object()
950 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
952 defp maybe_preload_bookmarks(query, opts) do
954 |> Activity.with_preloaded_bookmark(opts["user"])
957 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
959 defp maybe_set_thread_muted_field(query, opts) do
961 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
964 defp maybe_order(query, %{order: :desc}) do
966 |> order_by(desc: :id)
969 defp maybe_order(query, %{order: :asc}) do
971 |> order_by(asc: :id)
974 defp maybe_order(query, _), do: query
976 def fetch_activities_query(recipients, opts \\ %{}) do
978 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
982 |> maybe_preload_objects(opts)
983 |> maybe_preload_bookmarks(opts)
984 |> maybe_set_thread_muted_field(opts)
986 |> restrict_recipients(recipients, opts["user"])
987 |> restrict_tag(opts)
988 |> restrict_tag_reject(opts)
989 |> restrict_tag_all(opts)
990 |> restrict_since(opts)
991 |> restrict_local(opts)
992 |> restrict_actor(opts)
993 |> restrict_type(opts)
994 |> restrict_state(opts)
995 |> restrict_favorited_by(opts)
996 |> restrict_blocked(opts)
997 |> restrict_muted(opts)
998 |> restrict_media(opts)
999 |> restrict_visibility(opts)
1000 |> restrict_thread_visibility(opts, config)
1001 |> restrict_replies(opts)
1002 |> restrict_reblogs(opts)
1003 |> restrict_pinned(opts)
1004 |> restrict_muted_reblogs(opts)
1005 |> Activity.restrict_deactivated_users()
1006 |> exclude_poll_votes(opts)
1007 |> exclude_visibility(opts)
1010 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1011 list_memberships = Pleroma.List.memberships(opts["user"])
1013 fetch_activities_query(recipients ++ list_memberships, opts)
1014 |> Pagination.fetch_paginated(opts, pagination)
1016 |> maybe_update_cc(list_memberships, opts["user"])
1019 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1020 when is_list(list_memberships) and length(list_memberships) > 0 do
1021 Enum.map(activities, fn
1022 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1023 if Enum.any?(bcc, &(&1 in list_memberships)) do
1024 update_in(activity.data["cc"], &[user_ap_id | &1])
1034 defp maybe_update_cc(activities, _, _), do: activities
1036 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1037 from(activity in query,
1039 fragment("? && ?", activity.recipients, ^recipients) or
1040 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1041 ^Pleroma.Constants.as_public() in activity.recipients)
1045 def fetch_activities_bounded(
1047 recipients_with_public,
1049 pagination \\ :keyset
1051 fetch_activities_query([], opts)
1052 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1053 |> Pagination.fetch_paginated(opts, pagination)
1057 def upload(file, opts \\ []) do
1058 with {:ok, data} <- Upload.store(file, opts) do
1061 Map.put(data, "actor", opts[:actor])
1066 Repo.insert(%Object{data: obj_data})
1070 defp object_to_user_data(data) do
1072 data["icon"]["url"] &&
1075 "url" => [%{"href" => data["icon"]["url"]}]
1079 data["image"]["url"] &&
1082 "url" => [%{"href" => data["image"]["url"]}]
1087 |> Map.get("attachment", [])
1088 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1089 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1091 locked = data["manuallyApprovesFollowers"] || false
1092 data = Transmogrifier.maybe_fix_user_object(data)
1093 discoverable = data["discoverable"] || false
1103 discoverable: discoverable
1107 follower_address: data["followers"],
1108 following_address: data["following"],
1109 bio: data["summary"]
1112 # nickname can be nil because of virtual actors
1114 if data["preferredUsername"] do
1118 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1121 Map.put(user_data, :nickname, nil)
1127 def fetch_follow_information_for_user(user) do
1128 with {:ok, following_data} <-
1129 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1130 following_count when is_integer(following_count) <- following_data["totalItems"],
1131 {:ok, hide_follows} <- collection_private(following_data),
1132 {:ok, followers_data} <-
1133 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1134 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1135 {:ok, hide_followers} <- collection_private(followers_data) do
1138 hide_follows: hide_follows,
1139 follower_count: followers_count,
1140 following_count: following_count,
1141 hide_followers: hide_followers
1152 defp maybe_update_follow_information(data) do
1153 with {:enabled, true} <-
1154 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1155 {:ok, info} <- fetch_follow_information_for_user(data) do
1156 info = Map.merge(data.info, info)
1157 Map.put(data, :info, info)
1159 {:enabled, false} ->
1164 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1171 defp collection_private(data) do
1172 if is_map(data["first"]) and
1173 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1176 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1177 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1180 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1192 def user_data_from_user_object(data) do
1193 with {:ok, data} <- MRF.filter(data),
1194 {:ok, data} <- object_to_user_data(data) do
1201 def fetch_and_prepare_user_from_ap_id(ap_id) do
1202 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1203 {:ok, data} <- user_data_from_user_object(data),
1204 data <- maybe_update_follow_information(data) do
1207 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1211 def make_user_from_ap_id(ap_id) do
1212 if _user = User.get_cached_by_ap_id(ap_id) do
1213 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1215 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1216 User.insert_or_update_user(data)
1223 def make_user_from_nickname(nickname) do
1224 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1225 make_user_from_ap_id(ap_id)
1227 _e -> {:error, "No AP id in WebFinger"}
1231 # filter out broken threads
1232 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1233 entire_thread_visible_for_user?(activity, user)
1236 # do post-processing on a specific activity
1237 def contain_activity(%Activity{} = activity, %User{} = user) do
1238 contain_broken_threads(activity, user)
1241 def fetch_direct_messages_query do
1243 |> restrict_type(%{"type" => "Create"})
1244 |> restrict_visibility(%{visibility: "direct"})
1245 |> order_by([activity], asc: activity.id)