Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into emr_develop
This commit is contained in:
commit
3cbe72a98c
|
@ -1729,6 +1729,11 @@ config :pleroma, :config_description, [
|
|||
type: :boolean,
|
||||
description: "Sign object fetches with HTTP signatures"
|
||||
},
|
||||
%{
|
||||
key: :authorized_fetch_mode,
|
||||
type: :boolean,
|
||||
description: "Require HTTP signatures for AP fetches"
|
||||
},
|
||||
%{
|
||||
key: :note_replies_output_limit,
|
||||
type: :integer,
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Emoji do
|
|||
"""
|
||||
use GenServer
|
||||
|
||||
alias Pleroma.Emoji.Combinations
|
||||
alias Pleroma.Emoji.Loader
|
||||
|
||||
require Logger
|
||||
|
@ -137,4 +138,17 @@ defmodule Pleroma.Emoji do
|
|||
end
|
||||
|
||||
def is_unicode_emoji?(_), do: false
|
||||
|
||||
emoji_qualification_map =
|
||||
emojis
|
||||
|> Enum.filter(&String.contains?(&1, "\uFE0F"))
|
||||
|> Combinations.variate_emoji_qualification()
|
||||
|
||||
for {qualified, unqualified_list} <- emoji_qualification_map do
|
||||
for unqualified <- unqualified_list do
|
||||
def fully_qualify_emoji(unquote(unqualified)), do: unquote(qualified)
|
||||
end
|
||||
end
|
||||
|
||||
def fully_qualify_emoji(emoji), do: emoji
|
||||
end
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Emoji.Combinations do
|
||||
# FE0F is the emoji variation sequence. It is used for fully-qualifying
|
||||
# emoji, and that includes emoji combinations.
|
||||
# This code generates combinations per emoji: for each FE0F, all possible
|
||||
# combinations of the character being removed or staying will be generated.
|
||||
# This is made as an attempt to find all partially-qualified and unqualified
|
||||
# versions of a fully-qualified emoji.
|
||||
# I have found *no cases* for which this would be a problem, after browsing
|
||||
# the entire emoji list in emoji-test.txt. This is safe, and, sadly, most
|
||||
# likely sane too.
|
||||
|
||||
defp qualification_combinations(codepoints) do
|
||||
qualification_combinations([[]], codepoints)
|
||||
end
|
||||
|
||||
defp qualification_combinations(acc, []), do: acc
|
||||
|
||||
defp qualification_combinations(acc, ["\uFE0F" | tail]) do
|
||||
acc
|
||||
|> Enum.flat_map(fn x -> [x, x ++ ["\uFE0F"]] end)
|
||||
|> qualification_combinations(tail)
|
||||
end
|
||||
|
||||
defp qualification_combinations(acc, [codepoint | tail]) do
|
||||
acc
|
||||
|> Enum.map(&Kernel.++(&1, [codepoint]))
|
||||
|> qualification_combinations(tail)
|
||||
end
|
||||
|
||||
def variate_emoji_qualification(emoji) when is_binary(emoji) do
|
||||
emoji
|
||||
|> String.codepoints()
|
||||
|> qualification_combinations()
|
||||
|> Enum.map(&List.to_string/1)
|
||||
end
|
||||
|
||||
def variate_emoji_qualification(emoji) when is_list(emoji) do
|
||||
emoji
|
||||
|> Enum.map(fn emoji -> {emoji, variate_emoji_qualification(emoji)} end)
|
||||
end
|
||||
end
|
|
@ -1480,12 +1480,12 @@ defmodule Pleroma.User do
|
|||
{:ok, list(UserRelationship.t())} | {:error, String.t()}
|
||||
def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do
|
||||
notifications? = Map.get(params, :notifications, true)
|
||||
expires_in = Map.get(params, :expires_in, 0)
|
||||
duration = Map.get(params, :duration, 0)
|
||||
|
||||
expires_at =
|
||||
if expires_in > 0 do
|
||||
if duration > 0 do
|
||||
DateTime.utc_now()
|
||||
|> DateTime.add(expires_in)
|
||||
|> DateTime.add(duration)
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -1499,7 +1499,7 @@ defmodule Pleroma.User do
|
|||
expires_at
|
||||
)) ||
|
||||
{:ok, nil} do
|
||||
if expires_in > 0 do
|
||||
if duration > 0 do
|
||||
Pleroma.Workers.MuteExpireWorker.enqueue(
|
||||
"unmute_user",
|
||||
%{"muter_id" => muter.id, "mutee_id" => mutee.id},
|
||||
|
|
|
@ -63,8 +63,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|
|||
end
|
||||
|
||||
defp fix_emoji_qualification(%{"content" => emoji} = data) do
|
||||
# Emoji variation sequence
|
||||
new_emoji = emoji <> "\uFE0F"
|
||||
new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
|
||||
|
||||
cond do
|
||||
Pleroma.Emoji.is_unicode_emoji?(emoji) ->
|
||||
|
|
|
@ -278,11 +278,17 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
%Schema{allOf: [BooleanLike], default: true},
|
||||
"Mute notifications in addition to statuses? Defaults to `true`."
|
||||
),
|
||||
Operation.parameter(
|
||||
:duration,
|
||||
:query,
|
||||
%Schema{type: :integer},
|
||||
"Expire the mute in `duration` seconds. Default 0 for infinity"
|
||||
),
|
||||
Operation.parameter(
|
||||
:expires_in,
|
||||
:query,
|
||||
%Schema{type: :integer, default: 0},
|
||||
"Expire the mute in `expires_in` seconds. Default 0 for infinity"
|
||||
"Deprecated, use `duration` instead"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
|
@ -877,10 +883,15 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
description: "Mute notifications in addition to statuses? Defaults to true.",
|
||||
default: true
|
||||
},
|
||||
duration: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "Expire the mute in `expires_in` seconds. Default 0 for infinity"
|
||||
},
|
||||
expires_in: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
|
||||
description: "Deprecated, use `duration` instead",
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
|
|
|
@ -411,6 +411,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
@doc "POST /api/v1/accounts/:id/mute"
|
||||
def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put_new(:duration, Map.get(params, :expires_in, 0))
|
||||
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, params) do
|
||||
render(conn, "relationship.json", user: muter, target: muted)
|
||||
else
|
||||
|
@ -491,7 +495,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
users =
|
||||
user
|
||||
|> User.muted_users_relation(_restrict_deactivated = true)
|
||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||
|> Pleroma.Pagination.fetch_paginated(params)
|
||||
|
||||
conn
|
||||
|> add_link_headers(users)
|
||||
|
@ -509,7 +513,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
users =
|
||||
user
|
||||
|> User.blocked_users_relation(_restrict_deactivated = true)
|
||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||
|> Pleroma.Pagination.fetch_paginated(params)
|
||||
|
||||
conn
|
||||
|> add_link_headers(users)
|
||||
|
|
|
@ -5997,3 +5997,27 @@ msgstr ""
|
|||
msgctxt "config label at :web_push_encryption-:vapid_details > :subject"
|
||||
msgid "Subject"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-autogen, elixir-format
|
||||
#: lib/pleroma/docs/translator.ex:5
|
||||
msgctxt "config description at :pleroma-:activitypub > :authorized_fetch_mode"
|
||||
msgid "Require HTTP signatures for AP fetches"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-autogen, elixir-format
|
||||
#: lib/pleroma/docs/translator.ex:5
|
||||
msgctxt "config description at :pleroma-:instance > :short_description"
|
||||
msgid "Shorter version of instance description. It can be seen on `/api/v1/instance`"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-autogen, elixir-format
|
||||
#: lib/pleroma/docs/translator.ex:5
|
||||
msgctxt "config label at :pleroma-:activitypub > :authorized_fetch_mode"
|
||||
msgid "Authorized fetch mode"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-autogen, elixir-format
|
||||
#: lib/pleroma/docs/translator.ex:5
|
||||
msgctxt "config label at :pleroma-:instance > :short_description"
|
||||
msgid "Short description"
|
||||
msgstr ""
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"type": "EmojiReact",
|
||||
"signature": {
|
||||
"type": "RsaSignature2017",
|
||||
"signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
|
||||
"creator": "http://mastodon.example.org/users/admin#main-key",
|
||||
"created": "2018-02-17T18:57:49Z"
|
||||
},
|
||||
"object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454",
|
||||
"content": "❤",
|
||||
"nickname": "lain",
|
||||
"id": "http://mastodon.example.org/users/admin#reactions/2",
|
||||
"actor": "http://mastodon.example.org/users/admin",
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"sensitive": "as:sensitive",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"movedTo": "as:movedTo",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"Hashtag": "as:Hashtag",
|
||||
"Emoji": "toot:Emoji"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1146,7 +1146,7 @@ defmodule Pleroma.UserTest do
|
|||
user = insert(:user)
|
||||
muted_user = insert(:user)
|
||||
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60})
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user, %{duration: 60})
|
||||
assert User.mutes?(user, muted_user)
|
||||
|
||||
worker = Pleroma.Workers.MuteExpireWorker
|
||||
|
|
|
@ -42,11 +42,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do
|
|||
other_user = insert(:user, local: false)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
|
||||
|
||||
# woman detective emoji, unqualified
|
||||
unqualified_emoji = [0x1F575, 0x200D, 0x2640] |> List.to_string()
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/emoji-reaction-unqualified.json")
|
||||
File.read!("test/fixtures/emoji-reaction.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|> Map.put("actor", other_user.ap_id)
|
||||
|> Map.put("content", unqualified_emoji)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
|
@ -54,13 +58,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do
|
|||
assert data["type"] == "EmojiReact"
|
||||
assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2"
|
||||
assert data["object"] == activity.data["object"]
|
||||
# heart emoji with added emoji variation sequence
|
||||
assert data["content"] == "❤\uFE0F"
|
||||
# woman detective emoji, fully qualified
|
||||
emoji = [0x1F575, 0xFE0F, 0x200D, 0x2640, 0xFE0F] |> List.to_string()
|
||||
assert data["content"] == emoji
|
||||
|
||||
object = Object.get_by_ap_id(data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 1
|
||||
assert match?([["❤\uFE0F", _]], object.data["reactions"])
|
||||
assert match?([[emoji, _]], object.data["reactions"])
|
||||
end
|
||||
|
||||
test "it reject invalid emoji reactions" do
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
@ -1011,6 +1012,40 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
|
||||
json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
|
||||
test "expiring", %{conn: conn, user: user} do
|
||||
other_user = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v1/accounts/#{other_user.id}/mute", %{"duration" => "86400"})
|
||||
|
||||
assert %{"id" => _id, "muting" => true} = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
mute_expires_at = UserRelationship.get_mute_expire_date(user, other_user)
|
||||
|
||||
assert DateTime.diff(
|
||||
mute_expires_at,
|
||||
DateTime.utc_now() |> DateTime.add(24 * 60 * 60)
|
||||
) in -3..3
|
||||
end
|
||||
|
||||
test "falls back to expires_in", %{conn: conn, user: user} do
|
||||
other_user = insert(:user)
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v1/accounts/#{other_user.id}/mute", %{"expires_in" => "86400"})
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
mute_expires_at = UserRelationship.get_mute_expire_date(user, other_user)
|
||||
|
||||
assert DateTime.diff(
|
||||
mute_expires_at,
|
||||
DateTime.utc_now() |> DateTime.add(24 * 60 * 60)
|
||||
) in -3..3
|
||||
end
|
||||
end
|
||||
|
||||
describe "pinned statuses" do
|
||||
|
@ -1829,21 +1864,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
|> get("/api/v1/mutes")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [id1, id2, id3] == Enum.map(result, & &1["id"])
|
||||
assert [id3, id2, id1] == Enum.map(result, & &1["id"])
|
||||
|
||||
result =
|
||||
conn
|
||||
|> get("/api/v1/mutes?limit=1")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id1}] = result
|
||||
assert [%{"id" => ^id3}] = result
|
||||
|
||||
result =
|
||||
conn
|
||||
|> get("/api/v1/mutes?since_id=#{id1}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
|
||||
assert [%{"id" => ^id3}, %{"id" => ^id2}] = result
|
||||
|
||||
result =
|
||||
conn
|
||||
|
@ -1857,7 +1892,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
|> get("/api/v1/mutes?since_id=#{id1}&limit=1")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id2}] = result
|
||||
assert [%{"id" => ^id3}] = result
|
||||
end
|
||||
|
||||
test "list of mutes with with_relationships parameter" do
|
||||
|
@ -1876,7 +1911,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
|
||||
assert [
|
||||
%{
|
||||
"id" => ^id1,
|
||||
"id" => ^id3,
|
||||
"pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
|
||||
},
|
||||
%{
|
||||
|
@ -1884,7 +1919,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
"pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
|
||||
},
|
||||
%{
|
||||
"id" => ^id3,
|
||||
"id" => ^id1,
|
||||
"pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
|
||||
}
|
||||
] =
|
||||
|
@ -1909,7 +1944,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
|> get("/api/v1/blocks")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [id1, id2, id3] == Enum.map(result, & &1["id"])
|
||||
assert [id3, id2, id1] == Enum.map(result, & &1["id"])
|
||||
|
||||
result =
|
||||
conn
|
||||
|
@ -1917,7 +1952,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
|> get("/api/v1/blocks?limit=1")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id1}] = result
|
||||
assert [%{"id" => ^id3}] = result
|
||||
|
||||
result =
|
||||
conn
|
||||
|
@ -1925,7 +1960,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
|> get("/api/v1/blocks?since_id=#{id1}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
|
||||
assert [%{"id" => ^id3}, %{"id" => ^id2}] = result
|
||||
|
||||
result =
|
||||
conn
|
||||
|
@ -1941,7 +1976,31 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
|> get("/api/v1/blocks?since_id=#{id1}&limit=1")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id2}] = result
|
||||
assert [%{"id" => ^id3}] = result
|
||||
|
||||
conn_res =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/blocks?limit=2")
|
||||
|
||||
next_url =
|
||||
~r{<.+?(?<link>/api[^>]+)>; rel=\"next\"}
|
||||
|> Regex.named_captures(get_resp_header(conn_res, "link") |> Enum.at(0))
|
||||
|> Map.get("link")
|
||||
|
||||
result =
|
||||
conn_res
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id3}, %{"id" => ^id2}] = result
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get(next_url)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^id1}] = result
|
||||
end
|
||||
|
||||
test "account lookup", %{conn: conn} do
|
||||
|
|
|
@ -640,7 +640,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
|||
other_user = insert(:user)
|
||||
|
||||
{:ok, _user_relationships} =
|
||||
User.mute(user, other_user, %{notifications: true, expires_in: 24 * 60 * 60})
|
||||
User.mute(user, other_user, %{notifications: true, duration: 24 * 60 * 60})
|
||||
|
||||
%{
|
||||
mute_expires_at: mute_expires_at
|
||||
|
|
Loading…
Reference in New Issue