forked from AkkomaGang/akkoma
Merge branch 'feature/object-normalize-preload' into 'develop'
preloaded object normalization See merge request pleroma/pleroma!967
This commit is contained in:
commit
9b6b2395e4
17 changed files with 223 additions and 52 deletions
|
@ -7,6 +7,7 @@ defmodule Pleroma.Activity do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -33,9 +34,41 @@ defmodule Pleroma.Activity do
|
||||||
field(:recipients, {:array, :string})
|
field(:recipients, {:array, :string})
|
||||||
has_many(:notifications, Notification, on_delete: :delete_all)
|
has_many(:notifications, Notification, on_delete: :delete_all)
|
||||||
|
|
||||||
|
# Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
|
||||||
|
# The foreign key is embedded in a jsonb field.
|
||||||
|
#
|
||||||
|
# To use it, you probably want to do an inner join and a preload:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# |> join(:inner, [activity], o in Object,
|
||||||
|
# on: fragment("(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
# o.data, activity.data))
|
||||||
|
# |> preload([activity, object], [object: object])
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# As a convenience, Activity.with_preloaded_object() sets up an inner join and preload for the
|
||||||
|
# typical case.
|
||||||
|
has_one(:object, Object, on_delete: :nothing, foreign_key: :id)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_preloaded_object(query) do
|
||||||
|
query
|
||||||
|
|> join(
|
||||||
|
:inner,
|
||||||
|
[activity],
|
||||||
|
o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
o.data,
|
||||||
|
activity.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> preload([activity, object], object: object)
|
||||||
|
end
|
||||||
|
|
||||||
def get_by_ap_id(ap_id) do
|
def get_by_ap_id(ap_id) do
|
||||||
Repo.one(
|
Repo.one(
|
||||||
from(
|
from(
|
||||||
|
@ -45,10 +78,42 @@ def get_by_ap_id(ap_id) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_by_ap_id_with_object(ap_id) do
|
||||||
|
Repo.one(
|
||||||
|
from(
|
||||||
|
activity in Activity,
|
||||||
|
where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)),
|
||||||
|
left_join: o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
o.data,
|
||||||
|
activity.data
|
||||||
|
),
|
||||||
|
preload: [object: o]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def get_by_id(id) do
|
def get_by_id(id) do
|
||||||
Repo.get(Activity, id)
|
Repo.get(Activity, id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_by_id_with_object(id) do
|
||||||
|
from(activity in Activity,
|
||||||
|
where: activity.id == ^id,
|
||||||
|
inner_join: o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
o.data,
|
||||||
|
activity.data
|
||||||
|
),
|
||||||
|
preload: [object: o]
|
||||||
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
def by_object_ap_id(ap_id) do
|
def by_object_ap_id(ap_id) do
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
|
@ -76,7 +141,7 @@ def create_by_object_ap_id(ap_ids) when is_list(ap_ids) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_by_object_ap_id(ap_id) do
|
def create_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
where:
|
where:
|
||||||
|
@ -90,6 +155,8 @@ def create_by_object_ap_id(ap_id) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_by_object_ap_id(_), do: nil
|
||||||
|
|
||||||
def get_all_create_by_object_ap_id(ap_id) do
|
def get_all_create_by_object_ap_id(ap_id) do
|
||||||
Repo.all(create_by_object_ap_id(ap_id))
|
Repo.all(create_by_object_ap_id(ap_id))
|
||||||
end
|
end
|
||||||
|
@ -101,8 +168,38 @@ def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||||
|
|
||||||
def get_create_by_object_ap_id(_), do: nil
|
def get_create_by_object_ap_id(_), do: nil
|
||||||
|
|
||||||
def normalize(obj) when is_map(obj), do: Activity.get_by_ap_id(obj["id"])
|
def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do
|
||||||
def normalize(ap_id) when is_binary(ap_id), do: Activity.get_by_ap_id(ap_id)
|
from(
|
||||||
|
activity in Activity,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
|
activity.data,
|
||||||
|
activity.data,
|
||||||
|
^to_string(ap_id)
|
||||||
|
),
|
||||||
|
where: fragment("(?)->>'type' = 'Create'", activity.data),
|
||||||
|
inner_join: o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
o.data,
|
||||||
|
activity.data
|
||||||
|
),
|
||||||
|
preload: [object: o]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_by_object_ap_id_with_object(_), do: nil
|
||||||
|
|
||||||
|
def get_create_by_object_ap_id_with_object(ap_id) do
|
||||||
|
ap_id
|
||||||
|
|> create_by_object_ap_id_with_object()
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
|
||||||
|
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
|
||||||
def normalize(_), do: nil
|
def normalize(_), do: nil
|
||||||
|
|
||||||
def get_in_reply_to_activity(%Activity{data: %{"object" => %{"inReplyTo" => ap_id}}}) do
|
def get_in_reply_to_activity(%Activity{data: %{"object" => %{"inReplyTo" => ap_id}}}) do
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Notification do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -33,7 +34,15 @@ def for_user_query(user) do
|
||||||
Notification
|
Notification
|
||||||
|> where(user_id: ^user.id)
|
|> where(user_id: ^user.id)
|
||||||
|> join(:inner, [n], activity in assoc(n, :activity))
|
|> join(:inner, [n], activity in assoc(n, :activity))
|
||||||
|> preload(:activity)
|
|> join(:left, [n, a], object in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
object.data,
|
||||||
|
a.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> preload([n, a, o], activity: {a, object: o})
|
||||||
end
|
end
|
||||||
|
|
||||||
def for_user(user, opts \\ %{}) do
|
def for_user(user, opts \\ %{}) do
|
||||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.Object do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
schema "objects" do
|
schema "objects" do
|
||||||
field(:data, :map)
|
field(:data, :map)
|
||||||
|
|
||||||
|
@ -38,6 +40,33 @@ def get_by_ap_id(ap_id) do
|
||||||
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
|
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
|
||||||
|
# Use this whenever possible, especially when walking graphs in an O(N) loop!
|
||||||
|
def normalize(%Activity{object: %Object{} = object}), do: object
|
||||||
|
|
||||||
|
# Catch and log Object.normalize() calls where the Activity's child object is not
|
||||||
|
# preloaded.
|
||||||
|
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do
|
||||||
|
Logger.debug(
|
||||||
|
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
|
||||||
|
)
|
||||||
|
|
||||||
|
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
||||||
|
|
||||||
|
normalize(ap_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize(%Activity{data: %{"object" => ap_id}}) do
|
||||||
|
Logger.debug(
|
||||||
|
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
|
||||||
|
)
|
||||||
|
|
||||||
|
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
||||||
|
|
||||||
|
normalize(ap_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Old way, try fetching the object through cache.
|
||||||
def normalize(%{"id" => ap_id}), do: normalize(ap_id)
|
def normalize(%{"id" => ap_id}), do: normalize(ap_id)
|
||||||
def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id)
|
def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id)
|
||||||
def normalize(_), do: nil
|
def normalize(_), do: nil
|
||||||
|
|
|
@ -1110,13 +1110,15 @@ def delete(%User{} = user) do
|
||||||
friends
|
friends
|
||||||
|> Enum.each(fn followed -> User.unfollow(user, followed) end)
|
|> Enum.each(fn followed -> User.unfollow(user, followed) end)
|
||||||
|
|
||||||
query = from(a in Activity, where: a.actor == ^user.ap_id)
|
query =
|
||||||
|
from(a in Activity, where: a.actor == ^user.ap_id)
|
||||||
|
|> Activity.with_preloaded_object()
|
||||||
|
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
|> Enum.each(fn activity ->
|
|> Enum.each(fn activity ->
|
||||||
case activity.data["type"] do
|
case activity.data["type"] do
|
||||||
"Create" ->
|
"Create" ->
|
||||||
ActivityPub.delete(Object.normalize(activity.data["object"]))
|
ActivityPub.delete(Object.normalize(activity))
|
||||||
|
|
||||||
# TODO: Do something with likes, follows, repeats.
|
# TODO: Do something with likes, follows, repeats.
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -95,7 +95,7 @@ def insert(map, local \\ true) when is_map(map) do
|
||||||
:ok <- check_actor_is_active(map["actor"]),
|
:ok <- check_actor_is_active(map["actor"]),
|
||||||
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
||||||
{:ok, map} <- MRF.filter(map),
|
{:ok, map} <- MRF.filter(map),
|
||||||
:ok <- insert_full_object(map) do
|
{:ok, object} <- insert_full_object(map) do
|
||||||
{recipients, _, _} = get_recipients(map)
|
{recipients, _, _} = get_recipients(map)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
|
@ -106,6 +106,14 @@ def insert(map, local \\ true) when is_map(map) do
|
||||||
recipients: recipients
|
recipients: recipients
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Splice in the child object if we have one.
|
||||||
|
activity =
|
||||||
|
if !is_nil(object) do
|
||||||
|
Map.put(activity, :object, object)
|
||||||
|
else
|
||||||
|
activity
|
||||||
|
end
|
||||||
|
|
||||||
Task.start(fn ->
|
Task.start(fn ->
|
||||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||||
end)
|
end)
|
||||||
|
@ -430,6 +438,7 @@ def fetch_activities_for_context(context, opts \\ %{}) do
|
||||||
),
|
),
|
||||||
order_by: [desc: :id]
|
order_by: [desc: :id]
|
||||||
)
|
)
|
||||||
|
|> Activity.with_preloaded_object()
|
||||||
|
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
@ -709,6 +718,13 @@ defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
|
||||||
|
|
||||||
defp restrict_muted_reblogs(query, _), do: query
|
defp restrict_muted_reblogs(query, _), do: query
|
||||||
|
|
||||||
|
defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
|
||||||
|
|
||||||
|
defp maybe_preload_objects(query, _) do
|
||||||
|
query
|
||||||
|
|> Activity.with_preloaded_object()
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
base_query =
|
base_query =
|
||||||
from(
|
from(
|
||||||
|
@ -718,6 +734,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
)
|
)
|
||||||
|
|
||||||
base_query
|
base_query
|
||||||
|
|> maybe_preload_objects(opts)
|
||||||
|> restrict_recipients(recipients, opts["user"])
|
|> restrict_recipients(recipients, opts["user"])
|
||||||
|> restrict_tag(opts)
|
|> restrict_tag(opts)
|
||||||
|> restrict_tag_reject(opts)
|
|> restrict_tag_reject(opts)
|
||||||
|
@ -940,7 +957,7 @@ def fetch_object_from_id(id) do
|
||||||
},
|
},
|
||||||
:ok <- Transmogrifier.contain_origin(id, params),
|
:ok <- Transmogrifier.contain_origin(id, params),
|
||||||
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
|
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
|
||||||
{:ok, Object.normalize(activity.data["object"])}
|
{:ok, Object.normalize(activity)}
|
||||||
else
|
else
|
||||||
{:error, {:reject, nil}} ->
|
{:error, {:reject, nil}} ->
|
||||||
{:reject, nil}
|
{:reject, nil}
|
||||||
|
@ -952,7 +969,7 @@ def fetch_object_from_id(id) do
|
||||||
Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
|
Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
|
||||||
|
|
||||||
case OStatus.fetch_activity_from_url(id) do
|
case OStatus.fetch_activity_from_url(id) do
|
||||||
{:ok, [activity | _]} -> {:ok, Object.normalize(activity.data["object"])}
|
{:ok, [activity | _]} -> {:ok, Object.normalize(activity)}
|
||||||
e -> e
|
e -> e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,7 +41,7 @@ def unfollow(target_instance) do
|
||||||
|
|
||||||
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
with %User{} = user <- get_actor(),
|
with %User{} = user <- get_actor(),
|
||||||
%Object{} = object <- Object.normalize(activity.data["object"]["id"]) do
|
%Object{} = object <- Object.normalize(activity) do
|
||||||
ActivityPub.announce(user, object, nil, true, false)
|
ActivityPub.announce(user, object, nil, true, false)
|
||||||
else
|
else
|
||||||
e -> Logger.error("error: #{inspect(e)}")
|
e -> Logger.error("error: #{inspect(e)}")
|
||||||
|
|
|
@ -209,12 +209,12 @@ def lazy_put_object_defaults(map, activity \\ %{}) do
|
||||||
"""
|
"""
|
||||||
def insert_full_object(%{"object" => %{"type" => type} = object_data})
|
def insert_full_object(%{"object" => %{"type" => type} = object_data})
|
||||||
when is_map(object_data) and type in @supported_object_types do
|
when is_map(object_data) and type in @supported_object_types do
|
||||||
with {:ok, _} <- Object.create(object_data) do
|
with {:ok, object} <- Object.create(object_data) do
|
||||||
:ok
|
{:ok, object}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def insert_full_object(_), do: :ok
|
def insert_full_object(_), do: {:ok, nil}
|
||||||
|
|
||||||
def update_object_in_activities(%{data: %{"id" => id}} = object) do
|
def update_object_in_activities(%{data: %{"id" => id}} = object) do
|
||||||
# TODO
|
# TODO
|
||||||
|
|
|
@ -17,7 +17,7 @@ def render("object.json", %{object: %Object{} = object}) do
|
||||||
|
|
||||||
def render("object.json", %{object: %Activity{data: %{"type" => "Create"}} = activity}) do
|
def render("object.json", %{object: %Activity{data: %{"type" => "Create"}} = activity}) do
|
||||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
||||||
object = Object.normalize(activity.data["object"])
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
additional =
|
additional =
|
||||||
Transmogrifier.prepare_object(activity.data)
|
Transmogrifier.prepare_object(activity.data)
|
||||||
|
@ -28,7 +28,7 @@ def render("object.json", %{object: %Activity{data: %{"type" => "Create"}} = act
|
||||||
|
|
||||||
def render("object.json", %{object: %Activity{} = activity}) do
|
def render("object.json", %{object: %Activity{} = activity}) do
|
||||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
||||||
object = Object.normalize(activity.data["object"])
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
additional =
|
additional =
|
||||||
Transmogrifier.prepare_object(activity.data)
|
Transmogrifier.prepare_object(activity.data)
|
||||||
|
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.CommonAPI do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.ThreadMute
|
alias Pleroma.ThreadMute
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -64,8 +63,9 @@ def reject_follow_request(follower, followed) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(activity_id, user) do
|
def delete(activity_id, user) do
|
||||||
with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),
|
with %Activity{data: %{"object" => _}} = activity <-
|
||||||
%Object{} = object <- Object.normalize(object_id),
|
Activity.get_by_id_with_object(activity_id),
|
||||||
|
%Object{} = object <- Object.normalize(activity),
|
||||||
true <- User.superuser?(user) || user.ap_id == object.data["actor"],
|
true <- User.superuser?(user) || user.ap_id == object.data["actor"],
|
||||||
{:ok, _} <- unpin(activity_id, user),
|
{:ok, _} <- unpin(activity_id, user),
|
||||||
{:ok, delete} <- ActivityPub.delete(object) do
|
{:ok, delete} <- ActivityPub.delete(object) do
|
||||||
|
@ -75,7 +75,7 @@ def delete(activity_id, user) do
|
||||||
|
|
||||||
def repeat(id_or_ap_id, user) do
|
def repeat(id_or_ap_id, user) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
object <- Object.normalize(activity.data["object"]["id"]),
|
object <- Object.normalize(activity),
|
||||||
nil <- Utils.get_existing_announce(user.ap_id, object) do
|
nil <- Utils.get_existing_announce(user.ap_id, object) do
|
||||||
ActivityPub.announce(user, object)
|
ActivityPub.announce(user, object)
|
||||||
else
|
else
|
||||||
|
@ -86,7 +86,7 @@ def repeat(id_or_ap_id, user) do
|
||||||
|
|
||||||
def unrepeat(id_or_ap_id, user) do
|
def unrepeat(id_or_ap_id, user) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
object <- Object.normalize(activity.data["object"]["id"]) do
|
object <- Object.normalize(activity) do
|
||||||
ActivityPub.unannounce(user, object)
|
ActivityPub.unannounce(user, object)
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -96,7 +96,7 @@ def unrepeat(id_or_ap_id, user) do
|
||||||
|
|
||||||
def favorite(id_or_ap_id, user) do
|
def favorite(id_or_ap_id, user) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
object <- Object.normalize(activity.data["object"]["id"]),
|
object <- Object.normalize(activity),
|
||||||
nil <- Utils.get_existing_like(user.ap_id, object) do
|
nil <- Utils.get_existing_like(user.ap_id, object) do
|
||||||
ActivityPub.like(user, object)
|
ActivityPub.like(user, object)
|
||||||
else
|
else
|
||||||
|
@ -107,7 +107,7 @@ def favorite(id_or_ap_id, user) do
|
||||||
|
|
||||||
def unfavorite(id_or_ap_id, user) do
|
def unfavorite(id_or_ap_id, user) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
object <- Object.normalize(activity.data["object"]["id"]) do
|
object <- Object.normalize(activity) do
|
||||||
ActivityPub.unlike(user, object)
|
ActivityPub.unlike(user, object)
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -17,13 +17,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|
|
||||||
# This is a hack for twidere.
|
# This is a hack for twidere.
|
||||||
def get_by_id_or_ap_id(id) do
|
def get_by_id_or_ap_id(id) do
|
||||||
activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
|
activity =
|
||||||
|
Activity.get_by_id_with_object(id) || Activity.get_create_by_object_ap_id_with_object(id)
|
||||||
|
|
||||||
activity &&
|
activity &&
|
||||||
if activity.data["type"] == "Create" do
|
if activity.data["type"] == "Create" do
|
||||||
activity
|
activity
|
||||||
else
|
else
|
||||||
Activity.get_create_by_object_ap_id(activity.data["object"])
|
Activity.get_create_by_object_ap_id_with_object(activity.data["object"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -302,10 +303,10 @@ def maybe_notify_to_recipients(
|
||||||
|
|
||||||
def maybe_notify_mentioned_recipients(
|
def maybe_notify_mentioned_recipients(
|
||||||
recipients,
|
recipients,
|
||||||
%Activity{data: %{"to" => _to, "type" => type} = data} = _activity
|
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
|
||||||
)
|
)
|
||||||
when type == "Create" do
|
when type == "Create" do
|
||||||
object = Object.normalize(data["object"])
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
object_data =
|
object_data =
|
||||||
cond do
|
cond do
|
||||||
|
|
|
@ -106,7 +106,7 @@ def fetch_replied_to_activity(entry, in_reply_to) do
|
||||||
# TODO: Clean this up a bit.
|
# TODO: Clean this up a bit.
|
||||||
def handle_note(entry, doc \\ nil) do
|
def handle_note(entry, doc \\ nil) do
|
||||||
with id <- XML.string_from_xpath("//id", entry),
|
with id <- XML.string_from_xpath("//id", entry),
|
||||||
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id(id),
|
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
|
||||||
[author] <- :xmerl_xpath.string('//author[1]', doc),
|
[author] <- :xmerl_xpath.string('//author[1]', doc),
|
||||||
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
||||||
content_html <- OStatus.get_content(entry),
|
content_html <- OStatus.get_content(entry),
|
||||||
|
|
|
@ -23,8 +23,8 @@ defmodule Pleroma.Web.OStatus do
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Web.Websub
|
alias Pleroma.Web.Websub
|
||||||
|
|
||||||
def is_representable?(%Activity{data: data}) do
|
def is_representable?(%Activity{} = activity) do
|
||||||
object = Object.normalize(data["object"])
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
is_nil(object) ->
|
is_nil(object) ->
|
||||||
|
@ -119,7 +119,7 @@ def handle_incoming(xml_string) do
|
||||||
|
|
||||||
def make_share(entry, doc, retweeted_activity) do
|
def make_share(entry, doc, retweeted_activity) do
|
||||||
with {:ok, actor} <- find_make_or_update_user(doc),
|
with {:ok, actor} <- find_make_or_update_user(doc),
|
||||||
%Object{} = object <- Object.normalize(retweeted_activity.data["object"]),
|
%Object{} = object <- Object.normalize(retweeted_activity),
|
||||||
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
||||||
{:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
|
{:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -137,7 +137,7 @@ def handle_share(entry, doc) do
|
||||||
|
|
||||||
def make_favorite(entry, doc, favorited_activity) do
|
def make_favorite(entry, doc, favorited_activity) do
|
||||||
with {:ok, actor} <- find_make_or_update_user(doc),
|
with {:ok, actor} <- find_make_or_update_user(doc),
|
||||||
%Object{} = object <- Object.normalize(favorited_activity.data["object"]),
|
%Object{} = object <- Object.normalize(favorited_activity),
|
||||||
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
||||||
{:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
|
{:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -159,7 +159,7 @@ def get_or_try_fetching(entry) do
|
||||||
Logger.debug("Trying to get entry from db")
|
Logger.debug("Trying to get entry from db")
|
||||||
|
|
||||||
with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry),
|
with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry),
|
||||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
%Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -102,7 +102,8 @@ def object(conn, %{"uuid" => uuid}) do
|
||||||
ActivityPubController.call(conn, :object)
|
ActivityPubController.call(conn, :object)
|
||||||
else
|
else
|
||||||
with id <- o_status_url(conn, :object, uuid),
|
with id <- o_status_url(conn, :object, uuid),
|
||||||
{_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id(id)},
|
{_, %Activity{} = activity} <-
|
||||||
|
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||||
case get_format(conn) do
|
case get_format(conn) do
|
||||||
|
@ -148,13 +149,13 @@ def activity(conn, %{"uuid" => uuid}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def notice(conn, %{"id" => id}) do
|
def notice(conn, %{"id" => id}) do
|
||||||
with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(id)},
|
with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||||
case format = get_format(conn) do
|
case format = get_format(conn) do
|
||||||
"html" ->
|
"html" ->
|
||||||
if activity.data["type"] == "Create" do
|
if activity.data["type"] == "Create" do
|
||||||
%Object{} = object = Object.normalize(activity.data["object"])
|
%Object{} = object = Object.normalize(activity)
|
||||||
|
|
||||||
Fallback.RedirectController.redirector_with_meta(conn, %{
|
Fallback.RedirectController.redirector_with_meta(conn, %{
|
||||||
activity_id: activity.id,
|
activity_id: activity.id,
|
||||||
|
@ -191,9 +192,9 @@ def notice(conn, %{"id" => id}) do
|
||||||
|
|
||||||
# Returns an HTML embedded <audio> or <video> player suitable for embed iframes.
|
# Returns an HTML embedded <audio> or <video> player suitable for embed iframes.
|
||||||
def notice_player(conn, %{"id" => id}) do
|
def notice_player(conn, %{"id" => id}) do
|
||||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
||||||
true <- Visibility.is_public?(activity),
|
true <- Visibility.is_public?(activity),
|
||||||
%Object{} = object <- Object.normalize(activity.data["object"]),
|
%Object{} = object <- Object.normalize(activity),
|
||||||
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
||||||
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
||||||
conn
|
conn
|
||||||
|
@ -219,7 +220,7 @@ defp represent_activity(
|
||||||
%Activity{data: %{"type" => "Create"}} = activity,
|
%Activity{data: %{"type" => "Create"}} = activity,
|
||||||
_user
|
_user
|
||||||
) do
|
) do
|
||||||
object = Object.normalize(activity.data["object"])
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_header("content-type", "application/activity+json")
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|
|
|
@ -21,9 +21,9 @@ defp validate_page_url(%URI{scheme: nil}), do: :error
|
||||||
defp validate_page_url(%URI{}), do: :ok
|
defp validate_page_url(%URI{}), do: :ok
|
||||||
defp validate_page_url(_), do: :error
|
defp validate_page_url(_), do: :error
|
||||||
|
|
||||||
def fetch_data_for_activity(%Activity{} = activity) do
|
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
||||||
%Object{} = object <- Object.normalize(activity.data["object"]),
|
%Object{} = object <- Object.normalize(activity),
|
||||||
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
||||||
:ok <- validate_page_url(page_url),
|
:ok <- validate_page_url(page_url),
|
||||||
{:ok, rich_media} <- Parser.parse(page_url) do
|
{:ok, rich_media} <- Parser.parse(page_url) do
|
||||||
|
@ -32,4 +32,6 @@ def fetch_data_for_activity(%Activity{} = activity) do
|
||||||
_ -> %{}
|
_ -> %{}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_data_for_activity(_), do: %{}
|
||||||
end
|
end
|
||||||
|
|
|
@ -202,7 +202,7 @@ def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = ite
|
||||||
mutes = user.info.mutes || []
|
mutes = user.info.mutes || []
|
||||||
reblog_mutes = user.info.muted_reblogs || []
|
reblog_mutes = user.info.muted_reblogs || []
|
||||||
|
|
||||||
parent = Object.normalize(item.data["object"])
|
parent = Object.normalize(item)
|
||||||
|
|
||||||
unless is_nil(parent) or item.actor in blocks or item.actor in mutes or
|
unless is_nil(parent) or item.actor in blocks or item.actor in mutes or
|
||||||
item.actor in reblog_mutes or not ActivityPub.contain_activity(item, user) or
|
item.actor in reblog_mutes or not ActivityPub.contain_activity(item, user) or
|
||||||
|
|
|
@ -60,7 +60,8 @@ test "relay is unfollowed" do
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor_id" => follower_id,
|
"actor_id" => follower_id,
|
||||||
"limit" => 1
|
"limit" => 1,
|
||||||
|
"skip_preload" => true
|
||||||
})
|
})
|
||||||
|
|
||||||
assert undo_activity.data["type"] == "Undo"
|
assert undo_activity.data["type"] == "Undo"
|
||||||
|
|
|
@ -140,7 +140,7 @@ test "returns the activity if one with the same id is already in" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
{:ok, new_activity} = ActivityPub.insert(activity.data)
|
{:ok, new_activity} = ActivityPub.insert(activity.data)
|
||||||
|
|
||||||
assert activity == new_activity
|
assert activity.id == new_activity.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "inserts a given map into the activity database, giving it an id if it has none." do
|
test "inserts a given map into the activity database, giving it an id if it has none." do
|
||||||
|
@ -270,7 +270,8 @@ test "doesn't return blocked activities" do
|
||||||
booster = insert(:user)
|
booster = insert(:user)
|
||||||
{:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})
|
{:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -278,7 +279,8 @@ test "doesn't return blocked activities" do
|
||||||
|
|
||||||
{:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
|
{:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -289,14 +291,16 @@ test "doesn't return blocked activities" do
|
||||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||||
activity_three = Repo.get(Activity, activity_three.id)
|
activity_three = Repo.get(Activity, activity_three.id)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
refute Enum.member?(activities, activity_three)
|
refute Enum.member?(activities, activity_three)
|
||||||
refute Enum.member?(activities, boost_activity)
|
refute Enum.member?(activities, boost_activity)
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"blocking_user" => nil})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -312,14 +316,20 @@ test "doesn't return muted activities" do
|
||||||
booster = insert(:user)
|
booster = insert(:user)
|
||||||
{:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
|
{:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
refute Enum.member?(activities, activity_one)
|
refute Enum.member?(activities, activity_one)
|
||||||
|
|
||||||
# Calling with 'with_muted' will deliver muted activities, too.
|
# Calling with 'with_muted' will deliver muted activities, too.
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{
|
||||||
|
"muting_user" => user,
|
||||||
|
"with_muted" => true,
|
||||||
|
"skip_preload" => true
|
||||||
|
})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -327,7 +337,8 @@ test "doesn't return muted activities" do
|
||||||
|
|
||||||
{:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
|
{:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -338,14 +349,15 @@ test "doesn't return muted activities" do
|
||||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||||
activity_three = Repo.get(Activity, activity_three.id)
|
activity_three = Repo.get(Activity, activity_three.id)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
refute Enum.member?(activities, activity_three)
|
refute Enum.member?(activities, activity_three)
|
||||||
refute Enum.member?(activities, boost_activity)
|
refute Enum.member?(activities, boost_activity)
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => nil})
|
activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
|
Loading…
Reference in a new issue