[#58] pre-link MFM content #59

Merged
floatingghost merged 5 commits from prelink-mfm into develop 2022-07-10 17:06:26 +00:00
9 changed files with 175 additions and 18 deletions

View file

@ -36,6 +36,33 @@ config :logger, :console,
level: :info level: :info
``` ```
## Testing with HTTPS
If you end up developing alongside other software like misskey,
you will not be able to federate without an SSL certificate. You should
be able to use the snakeoil certificate that comes standard with most
distributions or generate one from scratch, then force elixir to accept it.
HTTP clients are none too keen to accept self-signed certs, but we can do
this:
```elixir
config :pleroma, :http,
adapter: [
pools: %{
default: [
conn_opts: [
transport_opts: [
verify: :verify_none
]
]
]
}
]
```
Now your SSL requests will work. Hooray.
## Testing ## Testing
1. Create a `test.secret.exs` file with the content as shown below 1. Create a `test.secret.exs` file with the content as shown below

View file

@ -58,9 +58,6 @@ def start(_type, _args) do
Pleroma.Docs.JSON.compile() Pleroma.Docs.JSON.compile()
limiters_setup() limiters_setup()
Logger.info("Starting Finch")
Finch.start_link(name: MyFinch)
# Define workers and child supervisors to be supervised # Define workers and child supervisors to be supervised
children = children =
[ [
@ -70,6 +67,7 @@ def start(_type, _args) do
Pleroma.Web.Plugs.RateLimiter.Supervisor Pleroma.Web.Plugs.RateLimiter.Supervisor
] ++ ] ++
cachex_children() ++ cachex_children() ++
http_children() ++
[ [
Pleroma.Stats, Pleroma.Stats,
Pleroma.JobQueueMonitor, Pleroma.JobQueueMonitor,
@ -276,4 +274,13 @@ def limiters_setup do
ConcurrentLimiter.new(module, max_running, max_waiting) ConcurrentLimiter.new(module, max_running, max_waiting)
end) end)
end end
defp http_children do
config =
[:http, :adapter]
|> Config.get([])
|> Keyword.put(:name, MyFinch)
[{Finch, config}]
end
end end

View file

@ -133,7 +133,7 @@ def html_escape(text, "text/html") do
HTML.filter_tags(text) HTML.filter_tags(text)
end end
def html_escape(text, "text/plain") do def html_escape(text, format) when format in ["text/plain", "text/x.misskeymarkdown"] do
Regex.split(@link_regex, text, include_captures: true) Regex.split(@link_regex, text, include_captures: true)
|> Enum.map_every(2, fn chunk -> |> Enum.map_every(2, fn chunk ->
{:safe, part} = Phoenix.HTML.html_escape(chunk) {:safe, part} = Phoenix.HTML.html_escape(chunk)

View file

@ -65,7 +65,6 @@ def request(method, url, body, headers, options) when is_binary(url) do
options = put_in(options[:adapter], adapter_opts) options = put_in(options[:adapter], adapter_opts)
params = options[:params] || [] params = options[:params] || []
request = build_request(method, headers, options, url, body, params) request = build_request(method, headers, options, url, body, params)
client = Tesla.client([Tesla.Middleware.FollowRedirects]) client = Tesla.client([Tesla.Middleware.FollowRedirects])
request(client, request) request(client, request)

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object.Fetcher alias Pleroma.Object.Fetcher
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
@ -81,12 +82,22 @@ defp fix_replies(%{"replies" => %{"first" => first}} = data) do
defp fix_replies(data), do: data defp fix_replies(data), do: data
# https://github.com/misskey-dev/misskey/pull/8787 # https://github.com/misskey-dev/misskey/pull/8787
defp fix_misskey_content(%{"source" => %{"mediaType" => "text/x.misskeymarkdown"}} = object), defp fix_misskey_content(
do: object %{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object
) do
{linked, _, _} = Utils.format_input(content, "text/x.misskeymarkdown")
Map.put(object, "content", linked)
end
defp fix_misskey_content(%{"_misskey_content" => content} = object) do defp fix_misskey_content(%{"_misskey_content" => content} = object) do
{linked, _, _} = Utils.format_input(content, "text/x.misskeymarkdown")
object object
|> Map.put("source", %{"content" => content, "mediaType" => "text/x.misskeymarkdown"}) |> Map.put("source", %{
"content" => content,
"mediaType" => "text/x.misskeymarkdown"
})
|> Map.put("content", linked)
|> Map.delete("_misskey_content") |> Map.delete("_misskey_content")
end end

View file

@ -259,7 +259,8 @@ def format_input(text, format, options \\ [])
@doc """ @doc """
Formatting text to plain text, BBCode, HTML, or Markdown Formatting text to plain text, BBCode, HTML, or Markdown
""" """
def format_input(text, "text/plain", options) do def format_input(text, format, options)
when format in ["text/plain", "text/x.misskeymarkdown"] do
text text
|> Formatter.html_escape("text/plain") |> Formatter.html_escape("text/plain")
|> Formatter.linkify(options) |> Formatter.linkify(options)
@ -291,15 +292,6 @@ def format_input(text, "text/markdown", options) do
|> Formatter.html_escape("text/html") |> Formatter.html_escape("text/html")
end end
def format_input(text, "text/x.misskeymarkdown", options) do
text
|> Formatter.html_escape("text/plain")
|> Formatter.linkify(options)
|> (fn {text, mentions, tags} ->
{String.replace(text, ~r/\r?\n/, "<br>"), mentions, tags}
end).()
end
def format_naive_asctime(date) do def format_naive_asctime(date) do
date |> DateTime.from_naive!("Etc/UTC") |> format_asctime date |> DateTime.from_naive!("Etc/UTC") |> format_asctime
end end

View file

@ -0,0 +1,31 @@
{
"id": "https://misskey.local.live/notes/92j1n3owja",
"type": "Note",
"attributedTo": "https://misskey.local.live/users/92hzkskwgy",
"summary": null,
"content": "<p><a href=\"https://akkoma.local.live/users/akkoma_user\" class=\"u-url mention\">@akkoma_user@akkoma.local.live</a><span> linkifylink </span><a href=\"https://misskey.local.live/tags/dancedance\" rel=\"tag\">#dancedance</a><span> </span><i><span> mfm goes here</span></i><span> <br><br>## aaa</span></p>",
"_misskey_content": "@akkoma_user linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
"published": "2022-07-10T15:37:36.368Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://misskey.local.live/users/92hzkskwgy/followers",
"http://localhost:4001/users/akkoma_user"
],
"inReplyTo": null,
"attachment": [],
"sensitive": false,
"tag": [
{
"type": "Hashtag",
"href": "https://misskey.local.live/tags/dancedance",
"name": "#dancedance"
},
{
"type": "Mention",
"href": "http://localhost:4001/users/akkoma_user",
"name": "@akkoma_user"
}
]
}

34
test/fixtures/misskey/mfm_x_format.json vendored Normal file
View file

@ -0,0 +1,34 @@
{
"id": "https://misskey.local.live/notes/92j1n3owja",
"type": "Note",
"attributedTo": "https://misskey.local.live/users/92hzkskwgy",
"summary": null,
"content": "<p><a href=\"https://akkoma.local.live/users/akkoma_user\" class=\"u-url mention\">@akkoma_user@akkoma.local.live</a><span> linkifylink </span><a href=\"https://misskey.local.live/tags/dancedance\" rel=\"tag\">#dancedance</a><span> </span><i><span> mfm goes here</span></i><span> <br><br>## aaa</span></p>",
"source": {
"content": "@akkoma_user linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
"mediaType": "text/x.misskeymarkdown"
},
"published": "2022-07-10T15:37:36.368Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://misskey.local.live/users/92hzkskwgy/followers",
"http://localhost:4001/users/akkoma_user"
],
"inReplyTo": null,
"attachment": [],
"sensitive": false,
"tag": [
{
"type": "Hashtag",
"href": "https://misskey.local.live/tags/dancedance",
"name": "#dancedance"
},
{
"type": "Mention",
"href": "http://localhost:4001/users/akkoma_user",
"name": "@akkoma_user"
}
]
}

View file

@ -10,6 +10,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
import Pleroma.Factory import Pleroma.Factory
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
describe "Notes" do describe "Notes" do
setup do setup do
user = insert(:user) user = insert(:user)
@ -63,5 +69,55 @@ test "a note with an attachment should work", _ do
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note) %{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end end
test "a misskey MFM status with a content field should work and be linked", _ do
local_user = insert(:user, %{nickname: "akkoma_user"})
insert(:user, %{ap_id: "https://misskey.local.live/users/92hzkskwgy"})
note =
"test/fixtures/misskey/mfm_x_format.json"
|> File.read!()
|> Jason.decode!()
expected_content =
"<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{local_user.id}\" href=\"#{local_user.ap_id}\" rel=\"ugc\">@<span>akkoma_user</span></a></span> linkifylink <a class=\"hashtag\" data-tag=\"dancedance\" href=\"http://localhost:4001/tag/dancedance\" rel=\"tag ugc\">#dancedance</a> $[jelly mfm goes here] <br><br>## aaa"
%{
valid?: true,
changes: %{
content: ^expected_content,
source: %{
"content" => "@akkoma_user linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
"mediaType" => "text/x.misskeymarkdown"
}
}
} = ArticleNotePageValidator.cast_and_validate(note)
end
test "a misskey MFM status with a _misskey_content field should work and be linked", _ do
local_user = insert(:user, %{nickname: "akkoma_user"})
insert(:user, %{ap_id: "https://misskey.local.live/users/92hzkskwgy"})
note =
"test/fixtures/misskey/mfm_underscore_format.json"
|> File.read!()
|> Jason.decode!()
expected_content =
"<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{local_user.id}\" href=\"#{local_user.ap_id}\" rel=\"ugc\">@<span>akkoma_user</span></a></span> linkifylink <a class=\"hashtag\" data-tag=\"dancedance\" href=\"http://localhost:4001/tag/dancedance\" rel=\"tag ugc\">#dancedance</a> $[jelly mfm goes here] <br><br>## aaa"
%{
valid?: true,
changes: %{
content: ^expected_content,
source: %{
"content" => "@akkoma_user linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
"mediaType" => "text/x.misskeymarkdown"
}
}
} = ArticleNotePageValidator.cast_and_validate(note)
end
end end
end end