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
8 alias Pleroma.Conversation
9 alias Pleroma.Notification
11 alias Pleroma.Object.Containment
12 alias Pleroma.Object.Fetcher
13 alias Pleroma.Pagination
17 alias Pleroma.Web.ActivityPub.MRF
18 alias Pleroma.Web.ActivityPub.Transmogrifier
19 alias Pleroma.Web.WebFinger
20 alias Pleroma.Workers.BackgroundWorker
23 import Pleroma.Web.ActivityPub.Utils
24 import Pleroma.Web.ActivityPub.Visibility
27 require Pleroma.Constants
29 defdelegate worker_args(queue), to: Pleroma.Workers.Helper
31 # For Announce activities, we filter the recipients based on following status for any actors
32 # that match actual users. See issue #164 for more information about why this is necessary.
33 defp get_recipients(%{"type" => "Announce"} = data) do
34 to = Map.get(data, "to", [])
35 cc = Map.get(data, "cc", [])
36 bcc = Map.get(data, "bcc", [])
37 actor = User.get_cached_by_ap_id(data["actor"])
40 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
41 case User.get_cached_by_ap_id(recipient) do
43 user -> User.following?(user, actor)
50 defp get_recipients(%{"type" => "Create"} = data) do
51 to = Map.get(data, "to", [])
52 cc = Map.get(data, "cc", [])
53 bcc = Map.get(data, "bcc", [])
54 actor = Map.get(data, "actor", [])
55 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
59 defp get_recipients(data) do
60 to = Map.get(data, "to", [])
61 cc = Map.get(data, "cc", [])
62 bcc = Map.get(data, "bcc", [])
63 recipients = Enum.concat([to, cc, bcc])
67 defp check_actor_is_active(actor) do
68 if not is_nil(actor) do
69 with user <- User.get_cached_by_ap_id(actor),
70 false <- user.info.deactivated do
80 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
81 limit = Config.get([:instance, :remote_limit])
82 String.length(content) <= limit
85 defp check_remote_limit(_), do: true
87 def increase_note_count_if_public(actor, object) do
88 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
91 def decrease_note_count_if_public(actor, object) do
92 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
95 def increase_replies_count_if_reply(%{
96 "object" => %{"inReplyTo" => reply_ap_id} = object,
99 if is_public?(object) do
100 Object.increase_replies_count(reply_ap_id)
104 def increase_replies_count_if_reply(_create_data), do: :noop
106 def decrease_replies_count_if_reply(%Object{
107 data: %{"inReplyTo" => reply_ap_id} = object
109 if is_public?(object) do
110 Object.decrease_replies_count(reply_ap_id)
114 def decrease_replies_count_if_reply(_object), do: :noop
116 def increase_poll_votes_if_vote(%{
117 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
120 Object.increase_vote_count(reply_ap_id, name)
123 def increase_poll_votes_if_vote(_create_data), do: :noop
125 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
126 with nil <- Activity.normalize(map),
127 map <- lazy_put_activity_defaults(map, fake),
128 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
129 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
130 {:ok, map} <- MRF.filter(map),
131 {recipients, _, _} = get_recipients(map),
132 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
133 :ok <- Containment.contain_child(map),
134 {:ok, map, object} <- insert_full_object(map) do
136 Repo.insert(%Activity{
140 recipients: recipients
143 # Splice in the child object if we have one.
145 if !is_nil(object) do
146 Map.put(activity, :object, object)
151 %{"op" => "fetch_data_for_activity", "activity_id" => activity.id}
152 |> BackgroundWorker.new(worker_args(:background))
155 Notification.create_notifications(activity)
159 |> Conversation.create_or_bump_for()
160 |> get_participations()
163 stream_out_participations(participations)
166 %Activity{} = activity ->
169 {:fake, true, map, recipients} ->
170 activity = %Activity{
174 recipients: recipients,
178 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
186 defp get_participations({:ok, %{participations: participations}}), do: participations
187 defp get_participations(_), do: []
189 def stream_out_participations(participations) do
192 |> Repo.preload(:user)
194 Enum.each(participations, fn participation ->
195 Pleroma.Web.Streamer.stream("participation", participation)
199 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
200 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
201 conversation = Repo.preload(conversation, :participations),
203 fetch_latest_activity_id_for_context(conversation.ap_id, %{
205 "blocking_user" => user
207 if last_activity_id do
208 stream_out_participations(conversation.participations)
213 def stream_out_participations(_, _), do: :noop
215 def stream_out(activity) do
216 if activity.data["type"] in ["Create", "Announce", "Delete"] do
217 object = Object.normalize(activity)
218 # Do not stream out poll replies
219 unless object.data["type"] == "Answer" do
220 Pleroma.Web.Streamer.stream("user", activity)
221 Pleroma.Web.Streamer.stream("list", activity)
223 if get_visibility(activity) == "public" do
224 Pleroma.Web.Streamer.stream("public", activity)
227 Pleroma.Web.Streamer.stream("public:local", activity)
230 if activity.data["type"] in ["Create"] do
232 |> Map.get("tag", [])
233 |> Enum.filter(fn tag -> is_bitstring(tag) end)
234 |> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
236 if object.data["attachment"] != [] do
237 Pleroma.Web.Streamer.stream("public:media", activity)
240 Pleroma.Web.Streamer.stream("public:local:media", activity)
245 if get_visibility(activity) == "direct",
246 do: Pleroma.Web.Streamer.stream("direct", activity)
252 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) 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(create_data, local, fake),
264 {:fake, false, activity} <- {:fake, fake, activity},
265 _ <- increase_replies_count_if_reply(create_data),
266 _ <- increase_poll_votes_if_vote(create_data),
267 # Changing note count prior to enqueuing federation task in order to avoid
268 # race conditions on updating user.info
269 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
270 :ok <- maybe_federate(activity) do
273 {:fake, true, activity} ->
281 def accept(%{to: to, actor: actor, object: object} = params) do
282 # only accept false as false value
283 local = !(params[:local] == false)
285 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
286 {:ok, activity} <- insert(data, local),
287 :ok <- maybe_federate(activity) do
292 def reject(%{to: to, actor: actor, object: object} = params) do
293 # only accept false as false value
294 local = !(params[:local] == false)
296 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
297 {:ok, activity} <- insert(data, local),
298 :ok <- maybe_federate(activity) do
303 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
304 # only accept false as false value
305 local = !(params[:local] == false)
314 {:ok, activity} <- insert(data, local),
315 :ok <- maybe_federate(activity) do
320 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
322 %User{ap_id: ap_id} = user,
323 %Object{data: %{"id" => _}} = object,
327 with nil <- get_existing_like(ap_id, object),
328 like_data <- make_like_data(user, object, activity_id),
329 {:ok, activity} <- insert(like_data, local),
330 {:ok, object} <- add_like_to_object(activity, object),
331 :ok <- maybe_federate(activity) do
332 {:ok, activity, object}
334 %Activity{} = activity -> {:ok, activity, object}
335 error -> {:error, error}
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_public?(object),
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, local \\ true) do
426 user = User.get_cached_by_ap_id(actor)
427 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
429 with {:ok, object, activity} <- Object.delete(object),
435 "deleted_activity_id" => activity && activity.id
437 {:ok, activity} <- insert(data, local, false),
438 stream_out_participations(object, user),
439 _ <- decrease_replies_count_if_reply(object),
440 # Changing note count prior to enqueuing federation task in order to avoid
441 # race conditions on updating user.info
442 {:ok, _actor} <- decrease_note_count_if_public(user, object),
443 :ok <- maybe_federate(activity) do
448 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
449 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
450 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
452 if unfollow_blocked do
453 follow_activity = fetch_latest_follow(blocker, blocked)
454 if follow_activity, do: unfollow(blocker, blocked, nil, local)
457 with true <- outgoing_blocks,
458 block_data <- make_block_data(blocker, blocked, activity_id),
459 {:ok, activity} <- insert(block_data, local),
460 :ok <- maybe_federate(activity) do
467 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
468 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
469 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
470 {:ok, activity} <- insert(unblock_data, local),
471 :ok <- maybe_federate(activity) do
485 # only accept false as false value
486 local = !(params[:local] == false)
487 forward = !(params[:forward] == false)
489 additional = params[:additional] || %{}
501 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
503 Map.merge(additional, %{"to" => [], "cc" => []})
506 with flag_data <- make_flag_data(params, additional),
507 {:ok, activity} <- insert(flag_data, local),
508 :ok <- maybe_federate(activity) do
509 Enum.each(User.all_superusers(), fn superuser ->
511 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
512 |> Pleroma.Emails.Mailer.deliver_async()
519 defp fetch_activities_for_context_query(context, opts) do
520 public = [Pleroma.Constants.as_public()]
523 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
525 from(activity in Activity)
526 |> maybe_preload_objects(opts)
527 |> maybe_preload_bookmarks(opts)
528 |> maybe_set_thread_muted_field(opts)
529 |> restrict_blocked(opts)
530 |> restrict_recipients(recipients, opts["user"])
534 "?->>'type' = ? and ?->>'context' = ?",
541 |> exclude_poll_votes(opts)
543 |> order_by([activity], desc: activity.id)
546 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
547 def fetch_activities_for_context(context, opts \\ %{}) do
549 |> fetch_activities_for_context_query(opts)
553 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
554 Pleroma.FlakeId.t() | nil
555 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
557 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
563 def fetch_public_activities(opts \\ %{}) do
564 q = fetch_activities_query([Pleroma.Constants.as_public()], opts)
567 |> restrict_unlisted()
568 |> Pagination.fetch_paginated(opts)
572 @valid_visibilities ~w[direct unlisted public private]
574 defp restrict_visibility(query, %{visibility: visibility})
575 when is_list(visibility) do
576 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
582 "activity_visibility(?, ?, ?) = ANY (?)",
592 Logger.error("Could not restrict visibility to #{visibility}")
596 defp restrict_visibility(query, %{visibility: visibility})
597 when visibility in @valid_visibilities do
601 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
605 defp restrict_visibility(_query, %{visibility: visibility})
606 when visibility not in @valid_visibilities do
607 Logger.error("Could not restrict visibility to #{visibility}")
610 defp restrict_visibility(query, _visibility), do: query
612 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
615 defp restrict_thread_visibility(
617 %{"user" => %User{info: %{skip_thread_containment: true}}},
622 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
625 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
629 defp restrict_thread_visibility(query, _, _), do: query
631 def fetch_user_activities(user, reading_user, params \\ %{}) do
634 |> Map.put("type", ["Create", "Announce"])
635 |> Map.put("user", reading_user)
636 |> Map.put("actor_id", user.ap_id)
637 |> Map.put("whole_db", true)
638 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
641 user_activities_recipients(%{
642 "godmode" => params["godmode"],
643 "reading_user" => reading_user
646 fetch_activities(recipients, params)
650 defp user_activities_recipients(%{"godmode" => true}) do
654 defp user_activities_recipients(%{"reading_user" => reading_user}) do
656 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
658 [Pleroma.Constants.as_public()]
662 defp restrict_since(query, %{"since_id" => ""}), do: query
664 defp restrict_since(query, %{"since_id" => since_id}) do
665 from(activity in query, where: activity.id > ^since_id)
668 defp restrict_since(query, _), do: query
670 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
671 raise "Can't use the child object without preloading!"
674 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
675 when is_list(tag_reject) and tag_reject != [] do
677 [_activity, object] in query,
678 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
682 defp restrict_tag_reject(query, _), do: query
684 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
685 raise "Can't use the child object without preloading!"
688 defp restrict_tag_all(query, %{"tag_all" => tag_all})
689 when is_list(tag_all) and tag_all != [] do
691 [_activity, object] in query,
692 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
696 defp restrict_tag_all(query, _), do: query
698 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
699 raise "Can't use the child object without preloading!"
702 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
704 [_activity, object] in query,
705 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
709 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
711 [_activity, object] in query,
712 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
716 defp restrict_tag(query, _), do: query
718 defp restrict_recipients(query, [], _user), do: query
720 defp restrict_recipients(query, recipients, nil) do
721 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
724 defp restrict_recipients(query, recipients, user) do
727 where: fragment("? && ?", ^recipients, activity.recipients),
728 or_where: activity.actor == ^user.ap_id
732 defp restrict_local(query, %{"local_only" => true}) do
733 from(activity in query, where: activity.local == true)
736 defp restrict_local(query, _), do: query
738 defp restrict_actor(query, %{"actor_id" => actor_id}) do
739 from(activity in query, where: activity.actor == ^actor_id)
742 defp restrict_actor(query, _), do: query
744 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
745 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
748 defp restrict_type(query, %{"type" => type}) do
749 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
752 defp restrict_type(query, _), do: query
754 defp restrict_state(query, %{"state" => state}) do
755 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
758 defp restrict_state(query, _), do: query
760 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
762 [_activity, object] in query,
763 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
767 defp restrict_favorited_by(query, _), do: query
769 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
770 raise "Can't use the child object without preloading!"
773 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
775 [_activity, object] in query,
776 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
780 defp restrict_media(query, _), do: query
782 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
785 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
789 defp restrict_replies(query, _), do: query
791 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
792 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
795 defp restrict_reblogs(query, _), do: query
797 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
799 defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
803 from([activity] in query,
804 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
805 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
808 unless opts["skip_preload"] do
809 from([thread_mute: tm] in query, where: is_nil(tm))
815 defp restrict_muted(query, _), do: query
817 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
818 blocks = info.blocks || []
819 domain_blocks = info.domain_blocks || []
822 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
825 [activity, object: o] in query,
826 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
827 where: fragment("not (? && ?)", activity.recipients, ^blocks),
830 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
835 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
836 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
840 defp restrict_blocked(query, _), do: query
842 defp restrict_unlisted(query) do
847 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
849 ^[Pleroma.Constants.as_public()]
854 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
855 from(activity in query, where: activity.id in ^ids)
858 defp restrict_pinned(query, _), do: query
860 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
861 muted_reblogs = info.muted_reblogs || []
867 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
875 defp restrict_muted_reblogs(query, _), do: query
877 defp exclude_poll_votes(query, %{"include_poll_votes" => "true"}), do: query
879 defp exclude_poll_votes(query, _) do
880 if has_named_binding?(query, :object) do
881 from([activity, object: o] in query,
882 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
889 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
890 from(activity in query, where: activity.id != ^id)
893 defp exclude_id(query, _), do: query
895 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
897 defp maybe_preload_objects(query, _) do
899 |> Activity.with_preloaded_object()
902 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
904 defp maybe_preload_bookmarks(query, opts) do
906 |> Activity.with_preloaded_bookmark(opts["user"])
909 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
911 defp maybe_set_thread_muted_field(query, opts) do
913 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
916 defp maybe_order(query, %{order: :desc}) do
918 |> order_by(desc: :id)
921 defp maybe_order(query, %{order: :asc}) do
923 |> order_by(asc: :id)
926 defp maybe_order(query, _), do: query
928 def fetch_activities_query(recipients, opts \\ %{}) do
930 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
934 |> maybe_preload_objects(opts)
935 |> maybe_preload_bookmarks(opts)
936 |> maybe_set_thread_muted_field(opts)
938 |> restrict_recipients(recipients, opts["user"])
939 |> restrict_tag(opts)
940 |> restrict_tag_reject(opts)
941 |> restrict_tag_all(opts)
942 |> restrict_since(opts)
943 |> restrict_local(opts)
944 |> restrict_actor(opts)
945 |> restrict_type(opts)
946 |> restrict_state(opts)
947 |> restrict_favorited_by(opts)
948 |> restrict_blocked(opts)
949 |> restrict_muted(opts)
950 |> restrict_media(opts)
951 |> restrict_visibility(opts)
952 |> restrict_thread_visibility(opts, config)
953 |> restrict_replies(opts)
954 |> restrict_reblogs(opts)
955 |> restrict_pinned(opts)
956 |> restrict_muted_reblogs(opts)
957 |> Activity.restrict_deactivated_users()
958 |> exclude_poll_votes(opts)
961 def fetch_activities(recipients, opts \\ %{}) do
962 list_memberships = Pleroma.List.memberships(opts["user"])
964 fetch_activities_query(recipients ++ list_memberships, opts)
965 |> Pagination.fetch_paginated(opts)
967 |> maybe_update_cc(list_memberships, opts["user"])
970 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
971 when is_list(list_memberships) and length(list_memberships) > 0 do
972 Enum.map(activities, fn
973 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
974 if Enum.any?(bcc, &(&1 in list_memberships)) do
975 update_in(activity.data["cc"], &[user_ap_id | &1])
985 defp maybe_update_cc(activities, _, _), do: activities
987 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
988 from(activity in query,
990 fragment("? && ?", activity.recipients, ^recipients) or
991 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
992 ^Pleroma.Constants.as_public() in activity.recipients)
996 def fetch_activities_bounded(recipients, recipients_with_public, opts \\ %{}) do
997 fetch_activities_query([], opts)
998 |> fetch_activities_bounded_query(recipients, recipients_with_public)
999 |> Pagination.fetch_paginated(opts)
1003 def upload(file, opts \\ []) do
1004 with {:ok, data} <- Upload.store(file, opts) do
1007 Map.put(data, "actor", opts[:actor])
1012 Repo.insert(%Object{data: obj_data})
1016 defp object_to_user_data(data) do
1018 data["icon"]["url"] &&
1021 "url" => [%{"href" => data["icon"]["url"]}]
1025 data["image"]["url"] &&
1028 "url" => [%{"href" => data["image"]["url"]}]
1033 |> Map.get("attachment", [])
1034 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1035 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1037 locked = data["manuallyApprovesFollowers"] || false
1038 data = Transmogrifier.maybe_fix_user_object(data)
1051 follower_address: data["followers"],
1052 following_address: data["following"],
1053 bio: data["summary"]
1056 # nickname can be nil because of virtual actors
1058 if data["preferredUsername"] do
1062 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1065 Map.put(user_data, :nickname, nil)
1071 def fetch_follow_information_for_user(user) do
1072 with {:ok, following_data} <-
1073 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1074 following_count when is_integer(following_count) <- following_data["totalItems"],
1075 {:ok, hide_follows} <- collection_private(following_data),
1076 {:ok, followers_data} <-
1077 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1078 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1079 {:ok, hide_followers} <- collection_private(followers_data) do
1082 hide_follows: hide_follows,
1083 follower_count: followers_count,
1084 following_count: following_count,
1085 hide_followers: hide_followers
1096 defp maybe_update_follow_information(data) do
1097 with {:enabled, true} <-
1098 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1099 {:ok, info} <- fetch_follow_information_for_user(data) do
1100 info = Map.merge(data.info, info)
1101 Map.put(data, :info, info)
1103 {:enabled, false} ->
1108 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1115 defp collection_private(data) do
1116 if is_map(data["first"]) and
1117 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1120 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1121 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1124 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1136 def user_data_from_user_object(data) do
1137 with {:ok, data} <- MRF.filter(data),
1138 {:ok, data} <- object_to_user_data(data) do
1145 def fetch_and_prepare_user_from_ap_id(ap_id) do
1146 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1147 {:ok, data} <- user_data_from_user_object(data),
1148 data <- maybe_update_follow_information(data) do
1151 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1155 def make_user_from_ap_id(ap_id) do
1156 if _user = User.get_cached_by_ap_id(ap_id) do
1157 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1159 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1160 User.insert_or_update_user(data)
1167 def make_user_from_nickname(nickname) do
1168 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1169 make_user_from_ap_id(ap_id)
1171 _e -> {:error, "No AP id in WebFinger"}
1175 # filter out broken threads
1176 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1177 entire_thread_visible_for_user?(activity, user)
1180 # do post-processing on a specific activity
1181 def contain_activity(%Activity{} = activity, %User{} = user) do
1182 contain_broken_threads(activity, user)
1185 def fetch_direct_messages_query do
1187 |> restrict_type(%{"type" => "Create"})
1188 |> restrict_visibility(%{visibility: "direct"})
1189 |> order_by([activity], asc: activity.id)