Merge branch 'develop' into feature/database-compaction
This commit is contained in:
commit
627e5a0a49
1271 changed files with 42114 additions and 70683 deletions
|
|
@ -1,9 +1,22 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Utils do
|
||||
alias Pleroma.{Repo, Web, Object, Activity, User, Notification}
|
||||
alias Pleroma.Web.Router.Helpers
|
||||
alias Ecto.Changeset
|
||||
alias Ecto.UUID
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Ecto.{Changeset, UUID}
|
||||
alias Pleroma.Web.Router.Helpers
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
require Logger
|
||||
|
||||
@supported_object_types ["Article", "Note", "Video", "Page"]
|
||||
|
|
@ -21,11 +34,25 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
Map.put(params, "actor", get_ap_id(params["actor"]))
|
||||
end
|
||||
|
||||
def determine_explicit_mentions(%{"tag" => tag} = _object) when is_list(tag) do
|
||||
tag
|
||||
|> Enum.filter(fn x -> is_map(x) end)
|
||||
|> Enum.filter(fn x -> x["type"] == "Mention" end)
|
||||
|> Enum.map(fn x -> x["href"] end)
|
||||
end
|
||||
|
||||
def determine_explicit_mentions(%{"tag" => tag} = object) when is_map(tag) do
|
||||
Map.put(object, "tag", [tag])
|
||||
|> determine_explicit_mentions()
|
||||
end
|
||||
|
||||
def determine_explicit_mentions(_), do: []
|
||||
|
||||
defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll
|
||||
defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll
|
||||
defp recipient_in_collection(_, _), do: false
|
||||
|
||||
def recipient_in_message(ap_id, params) do
|
||||
def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params) do
|
||||
cond do
|
||||
recipient_in_collection(ap_id, params["to"]) ->
|
||||
true
|
||||
|
|
@ -44,6 +71,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
!params["to"] && !params["cc"] && !params["bto"] && !params["bcc"] ->
|
||||
true
|
||||
|
||||
# if the message is sent from somebody the user is following, then assume it
|
||||
# is addressed to the recipient
|
||||
User.following?(recipient, actor) ->
|
||||
true
|
||||
|
||||
true ->
|
||||
false
|
||||
end
|
||||
|
|
@ -72,7 +104,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
%{
|
||||
"@context" => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"#{Web.base_url()}/schemas/litepub-0.1.jsonld"
|
||||
"#{Web.base_url()}/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "und"
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
|
@ -138,7 +173,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
_ -> 5
|
||||
end
|
||||
|
||||
Pleroma.Web.Federator.enqueue(:publish, activity, priority)
|
||||
Pleroma.Web.Federator.publish(activity, priority)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
|
@ -148,18 +183,26 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
Adds an id and a published data if they aren't there,
|
||||
also adds it to an included object
|
||||
"""
|
||||
def lazy_put_activity_defaults(map) do
|
||||
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
|
||||
|
||||
def lazy_put_activity_defaults(map, fake \\ false) do
|
||||
map =
|
||||
map
|
||||
|> Map.put_new_lazy("id", &generate_activity_id/0)
|
||||
|> Map.put_new_lazy("published", &make_date/0)
|
||||
|> Map.put_new("context", context)
|
||||
|> Map.put_new("context_id", context_id)
|
||||
unless fake do
|
||||
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
|
||||
|
||||
map
|
||||
|> Map.put_new_lazy("id", &generate_activity_id/0)
|
||||
|> Map.put_new_lazy("published", &make_date/0)
|
||||
|> Map.put_new("context", context)
|
||||
|> Map.put_new("context_id", context_id)
|
||||
else
|
||||
map
|
||||
|> Map.put_new("id", "pleroma:fakeid")
|
||||
|> Map.put_new_lazy("published", &make_date/0)
|
||||
|> Map.put_new("context", "pleroma:fakecontext")
|
||||
|> Map.put_new("context_id", -1)
|
||||
end
|
||||
|
||||
if is_map(map["object"]) do
|
||||
object = lazy_put_object_defaults(map["object"], map)
|
||||
object = lazy_put_object_defaults(map["object"], map, fake)
|
||||
%{map | "object" => object}
|
||||
else
|
||||
map
|
||||
|
|
@ -169,7 +212,18 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
@doc """
|
||||
Adds an id and published date if they aren't there.
|
||||
"""
|
||||
def lazy_put_object_defaults(map, activity \\ %{}) do
|
||||
def lazy_put_object_defaults(map, activity \\ %{}, fake)
|
||||
|
||||
def lazy_put_object_defaults(map, activity, true = _fake) do
|
||||
map
|
||||
|> Map.put_new_lazy("published", &make_date/0)
|
||||
|> Map.put_new("id", "pleroma:fake_object_id")
|
||||
|> Map.put_new("context", activity["context"])
|
||||
|> Map.put_new("fake", true)
|
||||
|> Map.put_new("context_id", activity["context_id"])
|
||||
end
|
||||
|
||||
def lazy_put_object_defaults(map, activity, _fake) do
|
||||
map
|
||||
|> Map.put_new_lazy("id", &generate_object_id/0)
|
||||
|> Map.put_new_lazy("published", &make_date/0)
|
||||
|
|
@ -187,18 +241,18 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
map
|
||||
|> Map.put("object", object.data["id"])
|
||||
|
||||
{:ok, map}
|
||||
{:ok, map, object}
|
||||
end
|
||||
end
|
||||
|
||||
def insert_full_object(map), do: {:ok, map}
|
||||
def insert_full_object(map), do: {:ok, map, nil}
|
||||
|
||||
def update_object_in_activities(%{data: %{"id" => id}} = object) do
|
||||
# TODO
|
||||
# Update activities that already had this. Could be done in a seperate process.
|
||||
# Alternatively, just don't do this and fetch the current object each time. Most
|
||||
# could probably be taken from cache.
|
||||
relevant_activities = Activity.all_by_object_ap_id(id)
|
||||
relevant_activities = Activity.get_all_create_by_object_ap_id(id)
|
||||
|
||||
Enum.map(relevant_activities, fn activity ->
|
||||
new_activity_data = activity.data |> Map.put("object", object.data)
|
||||
|
|
@ -231,13 +285,52 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
Repo.one(query)
|
||||
end
|
||||
|
||||
def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, activity_id) do
|
||||
@doc """
|
||||
Returns like activities targeting an object
|
||||
"""
|
||||
def get_object_likes(%{data: %{"id" => id}}) do
|
||||
query =
|
||||
from(
|
||||
activity in Activity,
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^id
|
||||
),
|
||||
where: fragment("(?)->>'type' = 'Like'", activity.data)
|
||||
)
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
def make_like_data(
|
||||
%User{ap_id: ap_id} = actor,
|
||||
%{data: %{"actor" => object_actor_id, "id" => id}} = object,
|
||||
activity_id
|
||||
) do
|
||||
object_actor = User.get_cached_by_ap_id(object_actor_id)
|
||||
|
||||
to =
|
||||
if Visibility.is_public?(object) do
|
||||
[actor.follower_address, object.data["actor"]]
|
||||
else
|
||||
[object.data["actor"]]
|
||||
end
|
||||
|
||||
cc =
|
||||
(object.data["to"] ++ (object.data["cc"] || []))
|
||||
|> List.delete(actor.ap_id)
|
||||
|> List.delete(object_actor.follower_address)
|
||||
|
||||
data = %{
|
||||
"type" => "Like",
|
||||
"actor" => ap_id,
|
||||
"object" => id,
|
||||
"to" => [actor.follower_address, object.data["actor"]],
|
||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"to" => to,
|
||||
"cc" => cc,
|
||||
"context" => object.data["context"]
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +343,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|> Map.put("#{property}_count", length(element))
|
||||
|> Map.put("#{property}s", element),
|
||||
changeset <- Changeset.change(object, data: new_data),
|
||||
{:ok, object} <- Repo.update(changeset),
|
||||
{:ok, object} <- Object.update_and_set_cache(changeset),
|
||||
_ <- update_object_in_activities(object) do
|
||||
{:ok, object}
|
||||
end
|
||||
|
|
@ -281,6 +374,25 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
@doc """
|
||||
Updates a follow activity's state (for locked accounts).
|
||||
"""
|
||||
def update_follow_state(
|
||||
%Activity{data: %{"actor" => actor, "object" => object, "state" => "pending"}} = activity,
|
||||
state
|
||||
) do
|
||||
try do
|
||||
Ecto.Adapters.SQL.query!(
|
||||
Repo,
|
||||
"UPDATE activities SET data = jsonb_set(data, '{state}', $1) WHERE data->>'type' = 'Follow' AND data->>'actor' = $2 AND data->>'object' = $3 AND data->>'state' = 'pending'",
|
||||
[state, actor, object]
|
||||
)
|
||||
|
||||
activity = Activity.get_by_id(activity.id)
|
||||
{:ok, activity}
|
||||
rescue
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def update_follow_state(%Activity{} = activity, state) do
|
||||
with new_data <-
|
||||
activity.data
|
||||
|
|
@ -296,7 +408,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"""
|
||||
def make_follow_data(
|
||||
%User{ap_id: follower_id},
|
||||
%User{ap_id: followed_id} = followed,
|
||||
%User{ap_id: followed_id} = _followed,
|
||||
activity_id
|
||||
) do
|
||||
data = %{
|
||||
|
|
@ -323,13 +435,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
activity.data
|
||||
),
|
||||
where: activity.actor == ^follower_id,
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"? @> ?",
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
^%{object: followed_id}
|
||||
activity.data,
|
||||
^followed_id
|
||||
),
|
||||
order_by: [desc: :id],
|
||||
order_by: [fragment("? desc nulls last", activity.id)],
|
||||
limit: 1
|
||||
)
|
||||
|
||||
|
|
@ -365,9 +479,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"""
|
||||
# for relayed messages, we only want to send to subscribers
|
||||
def make_announce_data(
|
||||
%User{ap_id: ap_id, nickname: nil} = user,
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Object{data: %{"id" => id}} = object,
|
||||
activity_id
|
||||
activity_id,
|
||||
false
|
||||
) do
|
||||
data = %{
|
||||
"type" => "Announce",
|
||||
|
|
@ -384,7 +499,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
def make_announce_data(
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Object{data: %{"id" => id}} = object,
|
||||
activity_id
|
||||
activity_id,
|
||||
true
|
||||
) do
|
||||
data = %{
|
||||
"type" => "Announce",
|
||||
|
|
@ -484,13 +600,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
activity.data
|
||||
),
|
||||
where: activity.actor == ^blocker_id,
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"? @> ?",
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
^%{object: blocked_id}
|
||||
activity.data,
|
||||
^blocked_id
|
||||
),
|
||||
order_by: [desc: :id],
|
||||
order_by: [fragment("? desc nulls last", activity.id)],
|
||||
limit: 1
|
||||
)
|
||||
|
||||
|
|
@ -534,4 +652,65 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
}
|
||||
|> Map.merge(additional)
|
||||
end
|
||||
|
||||
#### Flag-related helpers
|
||||
|
||||
def make_flag_data(params, additional) do
|
||||
status_ap_ids =
|
||||
Enum.map(params.statuses || [], fn
|
||||
%Activity{} = act -> act.data["id"]
|
||||
act when is_map(act) -> act["id"]
|
||||
act when is_binary(act) -> act
|
||||
end)
|
||||
|
||||
object = [params.account.ap_id] ++ status_ap_ids
|
||||
|
||||
%{
|
||||
"type" => "Flag",
|
||||
"actor" => params.actor.ap_id,
|
||||
"content" => params.content,
|
||||
"object" => object,
|
||||
"context" => params.context
|
||||
}
|
||||
|> Map.merge(additional)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Fetches the OrderedCollection/OrderedCollectionPage from `from`, limiting the amount of pages fetched after
|
||||
the first one to `pages_left` pages.
|
||||
If the amount of pages is higher than the collection has, it returns whatever was there.
|
||||
"""
|
||||
def fetch_ordered_collection(from, pages_left, acc \\ []) do
|
||||
with {:ok, response} <- Tesla.get(from),
|
||||
{:ok, collection} <- Poison.decode(response.body) do
|
||||
case collection["type"] do
|
||||
"OrderedCollection" ->
|
||||
# If we've encountered the OrderedCollection and not the page,
|
||||
# just call the same function on the page address
|
||||
fetch_ordered_collection(collection["first"], pages_left)
|
||||
|
||||
"OrderedCollectionPage" ->
|
||||
if pages_left > 0 do
|
||||
# There are still more pages
|
||||
if Map.has_key?(collection, "next") do
|
||||
# There are still more pages, go deeper saving what we have into the accumulator
|
||||
fetch_ordered_collection(
|
||||
collection["next"],
|
||||
pages_left - 1,
|
||||
acc ++ collection["orderedItems"]
|
||||
)
|
||||
else
|
||||
# No more pages left, just return whatever we already have
|
||||
acc ++ collection["orderedItems"]
|
||||
end
|
||||
else
|
||||
# Got the amount of pages needed, add them all to the accumulator
|
||||
acc ++ collection["orderedItems"]
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, "Not an OrderedCollection or OrderedCollectionPage"}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue