forked from AkkomaGang/akkoma
FloatingGhost
98cb255d12
OTP builds to 1.15 Changelog entry Ensure policies are fully loaded Fix :warn use main branch for linkify Fix warn in tests Migrations for phoenix 1.17 Revert "Migrations for phoenix 1.17" This reverts commit 6a3b2f15b74ea5e33150529385215b7a531f3999. Oban upgrade Add default empty whitelist mix format limit test to amd64 OTP 26 tests for 1.15 use OTP_VERSION tag baka just 1.15 Massive deps update Update locale, deps Mix format shell???? multiline??? ? max cases 1 use assert_recieve don't put_env in async tests don't async conn/fs tests mix format FIx some uploader issues Fix tests
202 lines
5.9 KiB
Elixir
202 lines
5.9 KiB
Elixir
# Pleroma: A lightweight social networking server
|
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
|
use Ecto.Schema
|
|
alias Pleroma.User
|
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
|
alias Pleroma.Web.CommonAPI.Utils
|
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
|
|
|
import Ecto.Changeset
|
|
|
|
require Logger
|
|
require Pleroma.Web.ActivityPub.Transmogrifier
|
|
|
|
@primary_key false
|
|
@derive Jason.Encoder
|
|
|
|
embedded_schema do
|
|
quote do
|
|
unquote do
|
|
import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
|
|
message_fields()
|
|
object_fields()
|
|
status_object_fields()
|
|
end
|
|
end
|
|
|
|
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
|
field(:source, :map)
|
|
field(:contentMap, :map)
|
|
end
|
|
|
|
def cast_and_apply(data) do
|
|
data
|
|
|> cast_data
|
|
|> apply_action(:insert)
|
|
end
|
|
|
|
def cast_and_validate(data) do
|
|
data
|
|
|> cast_data()
|
|
|> validate_data()
|
|
end
|
|
|
|
def cast_data(data) do
|
|
%__MODULE__{}
|
|
|> changeset(data)
|
|
end
|
|
|
|
defp fix_url(%{"url" => url} = data) when is_bitstring(url), do: data
|
|
defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"])
|
|
defp fix_url(data), do: data
|
|
|
|
defp fix_tag(%{"tag" => tag} = data) when is_list(tag) do
|
|
Map.put(data, "tag", Enum.filter(tag, &is_map/1))
|
|
end
|
|
|
|
defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag])
|
|
defp fix_tag(data), do: Map.drop(data, ["tag"])
|
|
|
|
defp fix_replies(%{"replies" => replies} = data) when is_list(replies), do: data
|
|
|
|
defp fix_replies(%{"replies" => %{"first" => first}} = data) do
|
|
with {:ok, replies} <- Akkoma.Collections.Fetcher.fetch_collection(first) do
|
|
Map.put(data, "replies", replies)
|
|
else
|
|
{:error, _} ->
|
|
Logger.error("Could not fetch replies for #{first}")
|
|
Map.put(data, "replies", [])
|
|
end
|
|
end
|
|
|
|
defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies),
|
|
do: Map.put(data, "replies", replies)
|
|
|
|
defp fix_replies(data), do: Map.delete(data, "replies")
|
|
|
|
defp remote_mention_resolver(
|
|
%{"id" => ap_id, "tag" => tags},
|
|
"@" <> nickname = mention,
|
|
buffer,
|
|
opts,
|
|
acc
|
|
) do
|
|
initial_host =
|
|
ap_id
|
|
|> URI.parse()
|
|
|> Map.get(:host)
|
|
|
|
with mention_tag <-
|
|
Enum.find(tags, fn t ->
|
|
t["type"] == "Mention" &&
|
|
(t["name"] == mention || mention == "#{t["name"]}@#{initial_host}")
|
|
end),
|
|
false <- is_nil(mention_tag),
|
|
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(mention_tag["href"]) do
|
|
link = Pleroma.Formatter.mention_tag(user, nickname, opts)
|
|
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
|
|
else
|
|
_ -> {buffer, acc}
|
|
end
|
|
end
|
|
|
|
# See https://akkoma.dev/FoundKeyGang/FoundKey/issues/343
|
|
# Misskey/Foundkey drops some of the custom formatting when it sends remotely
|
|
# So this basically reprocesses the MFM source
|
|
defp fix_misskey_content(
|
|
%{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object
|
|
)
|
|
when is_binary(content) do
|
|
mention_handler = fn nick, buffer, opts, acc ->
|
|
remote_mention_resolver(object, nick, buffer, opts, acc)
|
|
end
|
|
|
|
{linked, _, _} =
|
|
Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
|
|
|
|
Map.put(object, "content", linked)
|
|
end
|
|
|
|
# See https://github.com/misskey-dev/misskey/pull/8787
|
|
# This is for compatibility with older Misskey instances
|
|
defp fix_misskey_content(%{"_misskey_content" => content} = object) when is_binary(content) do
|
|
mention_handler = fn nick, buffer, opts, acc ->
|
|
remote_mention_resolver(object, nick, buffer, opts, acc)
|
|
end
|
|
|
|
{linked, _, _} =
|
|
Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
|
|
|
|
object
|
|
|> Map.put("source", %{
|
|
"content" => content,
|
|
"mediaType" => "text/x.misskeymarkdown"
|
|
})
|
|
|> Map.put("content", linked)
|
|
|> Map.delete("_misskey_content")
|
|
end
|
|
|
|
defp fix_misskey_content(data), do: data
|
|
|
|
defp fix_source(%{"source" => source} = object) when is_binary(source) do
|
|
object
|
|
|> Map.put("source", %{"content" => source})
|
|
end
|
|
|
|
defp fix_source(object), do: object
|
|
|
|
defp fix_content_map_languages(%{"contentMap" => content_map} = object)
|
|
when is_map(content_map) do
|
|
# Only allow valid languages
|
|
content_map =
|
|
content_map
|
|
|> Enum.reject(fn {lang, _content} ->
|
|
!Pleroma.ISO639.valid_alpha2?(lang)
|
|
end)
|
|
|> Enum.into(%{})
|
|
|
|
Map.put(object, "contentMap", content_map)
|
|
end
|
|
|
|
defp fix_content_map_languages(object), do: object
|
|
|
|
defp fix(data) do
|
|
data
|
|
|> CommonFixes.fix_actor()
|
|
|> CommonFixes.fix_object_defaults()
|
|
|> fix_url()
|
|
|> fix_tag()
|
|
|> fix_replies()
|
|
|> fix_source()
|
|
|> fix_misskey_content()
|
|
|> Transmogrifier.fix_attachments()
|
|
|> Transmogrifier.fix_emoji()
|
|
|> Transmogrifier.fix_content_map()
|
|
|> fix_content_map_languages()
|
|
end
|
|
|
|
def changeset(struct, data) do
|
|
data = fix(data)
|
|
|
|
struct
|
|
|> cast(data, __schema__(:fields) -- [:attachment, :tag])
|
|
|> cast_embed(:attachment)
|
|
|> cast_embed(:tag)
|
|
end
|
|
|
|
defp validate_data(data_cng) do
|
|
data_cng
|
|
|> validate_inclusion(:type, ["Article", "Note", "Page"])
|
|
|> validate_required([:id, :actor, :attributedTo, :type, :context])
|
|
|> CommonValidations.validate_any_presence([:cc, :to])
|
|
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|
|
|> CommonValidations.validate_actor_presence()
|
|
|> CommonValidations.validate_host_match()
|
|
end
|
|
end
|