preloaded object normalization See merge request pleroma/pleroma!967tags/v1.1.4
@@ -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 @@ defmodule Pleroma.Activity 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 @@ defmodule Pleroma.Activity 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 @@ defmodule Pleroma.Activity 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 @@ defmodule Pleroma.Activity 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 normalize(ap_id) when is_binary(ap_id), do: Activity.get_by_ap_id(ap_id) | |||||
def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do | |||||
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 @@ defmodule Pleroma.Notification 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 @@ defmodule Pleroma.Object 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 @@ defmodule Pleroma.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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.Relay 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 @@ defmodule Pleroma.Web.ActivityPub.Utils 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 | |||||
:ok | |||||
with {:ok, object} <- Object.create(object_data) do | |||||
{: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 @@ defmodule Pleroma.Web.ActivityPub.ObjectView 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 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do | |||||
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 @@ defmodule Pleroma.Web.CommonAPI 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), | |||||
%Object{} = object <- Object.normalize(object_id), | |||||
with %Activity{data: %{"object" => _}} = activity <- | |||||
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 @@ defmodule Pleroma.Web.CommonAPI 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 @@ defmodule Pleroma.Web.CommonAPI 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 @@ defmodule Pleroma.Web.CommonAPI 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 @@ defmodule Pleroma.Web.CommonAPI 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 @@ defmodule Pleroma.Web.CommonAPI.Utils do | |||||
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 @@ defmodule Pleroma.Web.OStatus.NoteHandler 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 | |||||
object = Object.normalize(data["object"]) | |||||
def is_representable?(%Activity{} = activity) do | |||||
object = Object.normalize(activity) | |||||
cond do | cond do | ||||
is_nil(object) -> | is_nil(object) -> | ||||
@@ -119,7 +119,7 @@ defmodule Pleroma.Web.OStatus 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 @@ defmodule Pleroma.Web.OStatus 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 @@ defmodule Pleroma.Web.OStatus 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 @@ defmodule Pleroma.Web.OStatus.OStatusController 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 @@ defmodule Pleroma.Web.OStatus.OStatusController 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 @@ defmodule Pleroma.Web.OStatus.OStatusController 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 @@ defmodule Pleroma.Web.OStatus.OStatusController do | |||||
%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 @@ defmodule Pleroma.Web.RichMedia.Helpers do | |||||
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 @@ defmodule Pleroma.Web.RichMedia.Helpers do | |||||
_ -> %{} | _ -> %{} | ||||
end | end | ||||
end | end | ||||
def fetch_data_for_activity(_), do: %{} | |||||
end | end |
@@ -202,7 +202,7 @@ defmodule Pleroma.Web.Streamer do | |||||
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 @@ defmodule Mix.Tasks.Pleroma.RelayTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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) | ||||