[#58] pre-link MFM content #59
9 changed files with 175 additions and 18 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
31
test/fixtures/misskey/mfm_underscore_format.json
vendored
Normal file
31
test/fixtures/misskey/mfm_underscore_format.json
vendored
Normal 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
34
test/fixtures/misskey/mfm_x_format.json
vendored
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue