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]
228 quick_insert? = Pleroma.Config.get([:env]) == :benchmark
232 %{to: to, actor: actor, published: published, context: context, object: object},
235 {:ok, activity} <- insert(create_data, local, fake),
236 {:fake, false, activity} <- {:fake, fake, activity},
237 _ <- increase_replies_count_if_reply(create_data),
238 _ <- increase_poll_votes_if_vote(create_data),
239 {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
240 # Changing note count prior to enqueuing federation task in order to avoid
241 # race conditions on updating user.info
242 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
243 :ok <- maybe_federate(activity) do
246 {:quick_insert, true, activity} ->
249 {:fake, true, activity} ->
257 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
258 additional = params[:additional] || %{}
259 # only accept false as false value
260 local = !(params[:local] == false)
261 published = params[:published]
265 %{to: to, actor: actor, published: published, context: context, object: object},
268 {:ok, activity} <- insert(listen_data, local),
269 :ok <- maybe_federate(activity) do
277 def accept(params) do
278 accept_or_reject("Accept", params)
281 def reject(params) do
282 accept_or_reject("Reject", params)
285 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
286 local = Map.get(params, :local, true)
287 activity_id = Map.get(params, :activity_id, nil)
290 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
291 |> Utils.maybe_put("id", activity_id),
292 {:ok, activity} <- insert(data, local),
293 :ok <- maybe_federate(activity) do
298 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
299 local = !(params[:local] == false)
300 activity_id = params[:activity_id]
309 data <- Utils.maybe_put(data, "id", activity_id),
310 {:ok, activity} <- insert(data, local),
311 :ok <- maybe_federate(activity) do
316 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
318 %User{ap_id: ap_id} = user,
319 %Object{data: %{"id" => _}} = object,
323 with nil <- get_existing_like(ap_id, object),
324 like_data <- make_like_data(user, object, activity_id),
325 {:ok, activity} <- insert(like_data, local),
326 {:ok, object} <- add_like_to_object(activity, object),
327 :ok <- maybe_federate(activity) do
328 {:ok, activity, object}
330 %Activity{} = activity -> {:ok, activity, object}
331 error -> {:error, error}
335 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
336 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
337 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
338 {:ok, unlike_activity} <- insert(unlike_data, local),
339 {:ok, _activity} <- Repo.delete(like_activity),
340 {:ok, object} <- remove_like_from_object(like_activity, object),
341 :ok <- maybe_federate(unlike_activity) do
342 {:ok, unlike_activity, like_activity, object}
349 %User{ap_id: _} = user,
350 %Object{data: %{"id" => _}} = object,
355 with true <- is_announceable?(object, user, public),
356 announce_data <- make_announce_data(user, object, activity_id, public),
357 {:ok, activity} <- insert(announce_data, local),
358 {:ok, object} <- add_announce_to_object(activity, object),
359 :ok <- maybe_federate(activity) do
360 {:ok, activity, object}
362 error -> {:error, error}
372 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
373 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
374 {:ok, unannounce_activity} <- insert(unannounce_data, local),
375 :ok <- maybe_federate(unannounce_activity),
376 {:ok, _activity} <- Repo.delete(announce_activity),
377 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
378 {:ok, unannounce_activity, object}
384 def follow(follower, followed, activity_id \\ nil, local \\ true) do
385 with data <- make_follow_data(follower, followed, activity_id),
386 {:ok, activity} <- insert(data, local),
387 :ok <- maybe_federate(activity),
388 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
393 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
394 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
395 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
396 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
397 {:ok, activity} <- insert(unfollow_data, local),
398 :ok <- maybe_federate(activity) do
403 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
405 "to" => [follower_address],
408 "object" => %{"type" => "Person", "id" => ap_id}
410 {:ok, activity} <- insert(data, true, true, true),
411 :ok <- maybe_federate(activity) do
416 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
417 local = Keyword.get(options, :local, true)
418 activity_id = Keyword.get(options, :activity_id, nil)
419 actor = Keyword.get(options, :actor, actor)
421 user = User.get_cached_by_ap_id(actor)
422 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
424 with {:ok, object, activity} <- Object.delete(object),
431 "deleted_activity_id" => activity && activity.id
433 |> maybe_put("id", activity_id),
434 {:ok, activity} <- insert(data, local, false),
435 stream_out_participations(object, user),
436 _ <- decrease_replies_count_if_reply(object),
437 # Changing note count prior to enqueuing federation task in order to avoid
438 # race conditions on updating user.info
439 {:ok, _actor} <- decrease_note_count_if_public(user, object),
440 :ok <- maybe_federate(activity) do
445 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
446 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
447 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
448 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
450 if unfollow_blocked do
451 follow_activity = fetch_latest_follow(blocker, blocked)
452 if follow_activity, do: unfollow(blocker, blocked, nil, local)
455 with true <- outgoing_blocks,
456 block_data <- make_block_data(blocker, blocked, activity_id),
457 {:ok, activity} <- insert(block_data, local),
458 :ok <- maybe_federate(activity) do
465 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
466 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
467 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
468 {:ok, activity} <- insert(unblock_data, local),
469 :ok <- maybe_federate(activity) do
474 @spec flag(map()) :: {:ok, Activity.t()} | any
484 # only accept false as false value
485 local = !(params[:local] == false)
486 forward = !(params[:forward] == false)
488 additional = params[:additional] || %{}
492 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
494 Map.merge(additional, %{"to" => [], "cc" => []})
497 with flag_data <- make_flag_data(params, additional),
498 {:ok, activity} <- insert(flag_data, local),
499 :ok <- maybe_federate(activity) do
500 Enum.each(User.all_superusers(), fn superuser ->
502 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
503 |> Pleroma.Emails.Mailer.deliver_async()
510 defp fetch_activities_for_context_query(context, opts) do
511 public = [Pleroma.Constants.as_public()]
514 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
516 from(activity in Activity)
517 |> maybe_preload_objects(opts)
518 |> maybe_preload_bookmarks(opts)
519 |> maybe_set_thread_muted_field(opts)
520 |> restrict_blocked(opts)
521 |> restrict_recipients(recipients, opts["user"])
525 "?->>'type' = ? and ?->>'context' = ?",
532 |> exclude_poll_votes(opts)
534 |> order_by([activity], desc: activity.id)
537 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
538 def fetch_activities_for_context(context, opts \\ %{}) do
540 |> fetch_activities_for_context_query(opts)
544 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
545 FlakeId.Ecto.CompatType.t() | nil
546 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
548 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
554 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
555 opts = Map.drop(opts, ["user"])
557 [Pleroma.Constants.as_public()]
558 |> fetch_activities_query(opts)
559 |> restrict_unlisted()
560 |> Pagination.fetch_paginated(opts, pagination)
564 @valid_visibilities ~w[direct unlisted public private]
566 defp restrict_visibility(query, %{visibility: visibility})
567 when is_list(visibility) do
568 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
574 "activity_visibility(?, ?, ?) = ANY (?)",
584 Logger.error("Could not restrict visibility to #{visibility}")
588 defp restrict_visibility(query, %{visibility: visibility})
589 when visibility in @valid_visibilities do
593 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
597 defp restrict_visibility(_query, %{visibility: visibility})
598 when visibility not in @valid_visibilities do
599 Logger.error("Could not restrict visibility to #{visibility}")
602 defp restrict_visibility(query, _visibility), do: query
604 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
605 when is_list(visibility) do
606 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
611 "activity_visibility(?, ?, ?) = ANY (?)",
619 Logger.error("Could not exclude visibility to #{visibility}")
624 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
625 when visibility in @valid_visibilities do
630 "activity_visibility(?, ?, ?) = ?",
639 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
640 when visibility not in @valid_visibilities do
641 Logger.error("Could not exclude visibility to #{visibility}")
645 defp exclude_visibility(query, _visibility), do: query
647 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
650 defp restrict_thread_visibility(
652 %{"user" => %User{info: %{skip_thread_containment: true}}},
657 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
660 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
664 defp restrict_thread_visibility(query, _, _), do: query
666 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
669 |> Map.put("user", reading_user)
670 |> Map.put("actor_id", user.ap_id)
671 |> Map.put("whole_db", true)
674 user_activities_recipients(%{
675 "godmode" => params["godmode"],
676 "reading_user" => reading_user
679 fetch_activities(recipients, params)
683 def fetch_user_activities(user, reading_user, params \\ %{}) do
686 |> Map.put("type", ["Create", "Announce"])
687 |> Map.put("user", reading_user)
688 |> Map.put("actor_id", user.ap_id)
689 |> Map.put("whole_db", true)
690 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
693 user_activities_recipients(%{
694 "godmode" => params["godmode"],
695 "reading_user" => reading_user
698 fetch_activities(recipients, params)
702 defp user_activities_recipients(%{"godmode" => true}) do
706 defp user_activities_recipients(%{"reading_user" => reading_user}) do
708 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
710 [Pleroma.Constants.as_public()]
714 defp restrict_since(query, %{"since_id" => ""}), do: query
716 defp restrict_since(query, %{"since_id" => since_id}) do
717 from(activity in query, where: activity.id > ^since_id)
720 defp restrict_since(query, _), do: query
722 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
723 raise "Can't use the child object without preloading!"
726 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
727 when is_list(tag_reject) and tag_reject != [] do
729 [_activity, object] in query,
730 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
734 defp restrict_tag_reject(query, _), do: query
736 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
737 raise "Can't use the child object without preloading!"
740 defp restrict_tag_all(query, %{"tag_all" => tag_all})
741 when is_list(tag_all) and tag_all != [] do
743 [_activity, object] in query,
744 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
748 defp restrict_tag_all(query, _), do: query
750 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
751 raise "Can't use the child object without preloading!"
754 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
756 [_activity, object] in query,
757 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
761 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
763 [_activity, object] in query,
764 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
768 defp restrict_tag(query, _), do: query
770 defp restrict_recipients(query, [], _user), do: query
772 defp restrict_recipients(query, recipients, nil) do
773 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
776 defp restrict_recipients(query, recipients, user) do
779 where: fragment("? && ?", ^recipients, activity.recipients),
780 or_where: activity.actor == ^user.ap_id
784 defp restrict_local(query, %{"local_only" => true}) do
785 from(activity in query, where: activity.local == true)
788 defp restrict_local(query, _), do: query
790 defp restrict_actor(query, %{"actor_id" => actor_id}) do
791 from(activity in query, where: activity.actor == ^actor_id)
794 defp restrict_actor(query, _), do: query
796 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
797 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
800 defp restrict_type(query, %{"type" => type}) do
801 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
804 defp restrict_type(query, _), do: query
806 defp restrict_state(query, %{"state" => state}) do
807 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
810 defp restrict_state(query, _), do: query
812 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
814 [_activity, object] in query,
815 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
819 defp restrict_favorited_by(query, _), do: query
821 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
822 raise "Can't use the child object without preloading!"
825 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
827 [_activity, object] in query,
828 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
832 defp restrict_media(query, _), do: query
834 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
836 [_activity, object] in query,
837 where: fragment("?->>'inReplyTo' is null", object.data)
841 defp restrict_replies(query, _), do: query
843 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
844 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
847 defp restrict_reblogs(query, _), do: query
849 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
851 defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
855 from([activity] in query,
856 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
857 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
860 unless opts["skip_preload"] do
861 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
867 defp restrict_muted(query, _), do: query
869 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
870 blocks = info.blocks || []
871 domain_blocks = info.domain_blocks || []
874 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
877 [activity, object: o] in query,
878 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
879 where: fragment("not (? && ?)", activity.recipients, ^blocks),
882 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
887 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
888 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
892 defp restrict_blocked(query, _), do: query
894 defp restrict_unlisted(query) do
899 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
901 ^[Pleroma.Constants.as_public()]
906 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
907 from(activity in query, where: activity.id in ^ids)
910 defp restrict_pinned(query, _), do: query
912 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
913 muted_reblogs = info.muted_reblogs || []
919 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
927 defp restrict_muted_reblogs(query, _), do: query
929 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
931 defp exclude_poll_votes(query, _) do
932 if has_named_binding?(query, :object) do
933 from([activity, object: o] in query,
934 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
941 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
942 from(activity in query, where: activity.id != ^id)
945 defp exclude_id(query, _), do: query
947 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
949 defp maybe_preload_objects(query, _) do
951 |> Activity.with_preloaded_object()
954 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
956 defp maybe_preload_bookmarks(query, opts) do
958 |> Activity.with_preloaded_bookmark(opts["user"])
961 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
963 defp maybe_set_thread_muted_field(query, opts) do
965 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
968 defp maybe_order(query, %{order: :desc}) do
970 |> order_by(desc: :id)
973 defp maybe_order(query, %{order: :asc}) do
975 |> order_by(asc: :id)
978 defp maybe_order(query, _), do: query
980 def fetch_activities_query(recipients, opts \\ %{}) do
982 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
986 |> maybe_preload_objects(opts)
987 |> maybe_preload_bookmarks(opts)
988 |> maybe_set_thread_muted_field(opts)
990 |> restrict_recipients(recipients, opts["user"])
991 |> restrict_tag(opts)
992 |> restrict_tag_reject(opts)
993 |> restrict_tag_all(opts)
994 |> restrict_since(opts)
995 |> restrict_local(opts)
996 |> restrict_actor(opts)
997 |> restrict_type(opts)
998 |> restrict_state(opts)
999 |> restrict_favorited_by(opts)
1000 |> restrict_blocked(opts)
1001 |> restrict_muted(opts)
1002 |> restrict_media(opts)
1003 |> restrict_visibility(opts)
1004 |> restrict_thread_visibility(opts, config)
1005 |> restrict_replies(opts)
1006 |> restrict_reblogs(opts)
1007 |> restrict_pinned(opts)
1008 |> restrict_muted_reblogs(opts)
1009 |> Activity.restrict_deactivated_users()
1010 |> exclude_poll_votes(opts)
1011 |> exclude_visibility(opts)
1014 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1015 list_memberships = Pleroma.List.memberships(opts["user"])
1017 fetch_activities_query(recipients ++ list_memberships, opts)
1018 |> Pagination.fetch_paginated(opts, pagination)
1020 |> maybe_update_cc(list_memberships, opts["user"])
1023 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1024 when is_list(list_memberships) and length(list_memberships) > 0 do
1025 Enum.map(activities, fn
1026 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1027 if Enum.any?(bcc, &(&1 in list_memberships)) do
1028 update_in(activity.data["cc"], &[user_ap_id | &1])
1038 defp maybe_update_cc(activities, _, _), do: activities
1040 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1041 from(activity in query,
1043 fragment("? && ?", activity.recipients, ^recipients) or
1044 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1045 ^Pleroma.Constants.as_public() in activity.recipients)
1049 def fetch_activities_bounded(
1051 recipients_with_public,
1053 pagination \\ :keyset
1055 fetch_activities_query([], opts)
1056 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1057 |> Pagination.fetch_paginated(opts, pagination)
1061 def upload(file, opts \\ []) do
1062 with {:ok, data} <- Upload.store(file, opts) do
1065 Map.put(data, "actor", opts[:actor])
1070 Repo.insert(%Object{data: obj_data})
1074 defp object_to_user_data(data) do
1076 data["icon"]["url"] &&
1079 "url" => [%{"href" => data["icon"]["url"]}]
1083 data["image"]["url"] &&
1086 "url" => [%{"href" => data["image"]["url"]}]
1091 |> Map.get("attachment", [])
1092 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1093 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1095 locked = data["manuallyApprovesFollowers"] || false
1096 data = Transmogrifier.maybe_fix_user_object(data)
1097 discoverable = data["discoverable"] || false
1107 discoverable: discoverable
1111 follower_address: data["followers"],
1112 following_address: data["following"],
1113 bio: data["summary"]
1116 # nickname can be nil because of virtual actors
1118 if data["preferredUsername"] do
1122 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1125 Map.put(user_data, :nickname, nil)
1131 def fetch_follow_information_for_user(user) do
1132 with {:ok, following_data} <-
1133 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1134 following_count when is_integer(following_count) <- following_data["totalItems"],
1135 {:ok, hide_follows} <- collection_private(following_data),
1136 {:ok, followers_data} <-
1137 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1138 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1139 {:ok, hide_followers} <- collection_private(followers_data) do
1142 hide_follows: hide_follows,
1143 follower_count: followers_count,
1144 following_count: following_count,
1145 hide_followers: hide_followers
1156 defp maybe_update_follow_information(data) do
1157 with {:enabled, true} <-
1158 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1159 {:ok, info} <- fetch_follow_information_for_user(data) do
1160 info = Map.merge(data.info, info)
1161 Map.put(data, :info, info)
1163 {:enabled, false} ->
1168 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1175 defp collection_private(data) do
1176 if is_map(data["first"]) and
1177 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1180 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1181 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1184 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1196 def user_data_from_user_object(data) do
1197 with {:ok, data} <- MRF.filter(data),
1198 {:ok, data} <- object_to_user_data(data) do
1205 def fetch_and_prepare_user_from_ap_id(ap_id) do
1206 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1207 {:ok, data} <- user_data_from_user_object(data),
1208 data <- maybe_update_follow_information(data) do
1211 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1215 def make_user_from_ap_id(ap_id) do
1216 if _user = User.get_cached_by_ap_id(ap_id) do
1217 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1219 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1220 User.insert_or_update_user(data)
1227 def make_user_from_nickname(nickname) do
1228 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1229 make_user_from_ap_id(ap_id)
1231 _e -> {:error, "No AP id in WebFinger"}
1235 # filter out broken threads
1236 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1237 entire_thread_visible_for_user?(activity, user)
1240 # do post-processing on a specific activity
1241 def contain_activity(%Activity{} = activity, %User{} = user) do
1242 contain_broken_threads(activity, user)
1245 def fetch_direct_messages_query do
1247 |> restrict_type(%{"type" => "Create"})
1248 |> restrict_visibility(%{visibility: "direct"})
1249 |> order_by([activity], asc: activity.id)