Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into pleroma-fix-3241
This commit is contained in:
commit
0450da88b6
|
@ -0,0 +1 @@
|
||||||
|
Include following/followers in backups
|
|
@ -0,0 +1 @@
|
||||||
|
Add ForceMention MRF
|
|
@ -0,0 +1 @@
|
||||||
|
Video framegrabs were not working correctly after the change to use Exile to execute ffmpeg
|
|
@ -0,0 +1 @@
|
||||||
|
Add contact account to InstanceView
|
|
@ -0,0 +1 @@
|
||||||
|
Verify profile link ownership with rel="me"
|
|
@ -415,6 +415,10 @@
|
||||||
|
|
||||||
config :pleroma, :mrf_inline_quote, template: "<bdi>RT:</bdi> {url}"
|
config :pleroma, :mrf_inline_quote, template: "<bdi>RT:</bdi> {url}"
|
||||||
|
|
||||||
|
config :pleroma, :mrf_force_mention,
|
||||||
|
mention_parent: true,
|
||||||
|
mention_quoted: true
|
||||||
|
|
||||||
config :pleroma, :rich_media,
|
config :pleroma, :rich_media,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
ignore_hosts: [],
|
ignore_hosts: [],
|
||||||
|
|
|
@ -566,6 +566,20 @@
|
||||||
"Cool instance"
|
"Cool instance"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :status_page,
|
||||||
|
type: :string,
|
||||||
|
description: "A page where people can see the status of the server during an outage",
|
||||||
|
suggestions: [
|
||||||
|
"https://status.pleroma.example.org"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :contact_username,
|
||||||
|
type: :string,
|
||||||
|
description: "Instance owner username",
|
||||||
|
suggestions: ["admin"]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :limit,
|
key: :limit,
|
||||||
type: :integer,
|
type: :integer,
|
||||||
|
|
|
@ -161,7 +161,8 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
* `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)).
|
* `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)).
|
||||||
* `Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent`: Forces every mentioned user to be reflected in the post content.
|
* `Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent`: Forces every mentioned user to be reflected in the post content.
|
||||||
* `Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy`: Forces quote post URLs to be reflected in the message content inline.
|
* `Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy`: Forces quote post URLs to be reflected in the message content inline.
|
||||||
* `Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy`: Force a Link tag for posts quoting another post. (may break outgoing federation of quote posts with older Pleroma versions)
|
* `Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy`: Force a Link tag for posts quoting another post. (may break outgoing federation of quote posts with older Pleroma versions).
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.ForceMention`: Forces posts to include a mention of the author of parent post or the author of quoted post.
|
||||||
* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
||||||
* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
|
* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
|
||||||
|
|
||||||
|
@ -272,6 +273,10 @@ Notes:
|
||||||
#### :mrf_inline_quote
|
#### :mrf_inline_quote
|
||||||
* `template`: The template to append to the post. `{url}` will be replaced with the actual link to the quoted post. Default: `<bdi>RT:</bdi> {url}`
|
* `template`: The template to append to the post. `{url}` will be replaced with the actual link to the quoted post. Default: `<bdi>RT:</bdi> {url}`
|
||||||
|
|
||||||
|
#### :mrf_force_mention
|
||||||
|
* `mention_parent`: Whether to append mention of parent post author
|
||||||
|
* `mention_quoted`: Whether to append mention of parent quoted author
|
||||||
|
|
||||||
### :activitypub
|
### :activitypub
|
||||||
* `unfollow_blocked`: Whether blocks result in people getting unfollowed
|
* `unfollow_blocked`: Whether blocks result in people getting unfollowed
|
||||||
* `outgoing_blocks`: Whether to federate blocks to other instances
|
* `outgoing_blocks`: Whether to federate blocks to other instances
|
||||||
|
|
|
@ -40,28 +40,32 @@ def image_resize(url, options) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Note: video thumbnail is intentionally not resized (always has original dimensions)
|
# Note: video thumbnail is intentionally not resized (always has original dimensions)
|
||||||
|
@spec video_framegrab(String.t()) :: {:ok, binary()} | {:error, any()}
|
||||||
def video_framegrab(url) do
|
def video_framegrab(url) do
|
||||||
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
||||||
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
||||||
{:ok, pid} <- StringIO.open(env.body) do
|
{:ok, pid} <- StringIO.open(env.body) do
|
||||||
body_stream = IO.binstream(pid, 1)
|
body_stream = IO.binstream(pid, 1)
|
||||||
|
|
||||||
Exile.stream!(
|
result =
|
||||||
[
|
Exile.stream!(
|
||||||
executable,
|
[
|
||||||
"-i",
|
executable,
|
||||||
"pipe:0",
|
"-i",
|
||||||
"-vframes",
|
"pipe:0",
|
||||||
"1",
|
"-vframes",
|
||||||
"-f",
|
"1",
|
||||||
"mjpeg",
|
"-f",
|
||||||
"pipe:1"
|
"mjpeg",
|
||||||
],
|
"pipe:1"
|
||||||
input: body_stream,
|
],
|
||||||
ignore_epipe: true,
|
input: body_stream,
|
||||||
stderr: :disable
|
ignore_epipe: true,
|
||||||
)
|
stderr: :disable
|
||||||
|> Enum.into(<<>>)
|
)
|
||||||
|
|> Enum.into(<<>>)
|
||||||
|
|
||||||
|
{:ok, result}
|
||||||
else
|
else
|
||||||
nil -> {:error, {:ffmpeg, :command_not_found}}
|
nil -> {:error, {:ffmpeg, :command_not_found}}
|
||||||
{:error, _} = error -> error
|
{:error, _} = error -> error
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.User do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Ecto, only: [assoc: 2]
|
import Ecto, only: [assoc: 2]
|
||||||
|
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||||
|
|
||||||
alias Ecto.Multi
|
alias Ecto.Multi
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
@ -596,9 +597,23 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
|
|
||||||
defp put_fields(changeset) do
|
defp put_fields(changeset) do
|
||||||
if raw_fields = get_change(changeset, :raw_fields) do
|
if raw_fields = get_change(changeset, :raw_fields) do
|
||||||
|
old_fields = changeset.data.raw_fields
|
||||||
|
|
||||||
raw_fields =
|
raw_fields =
|
||||||
raw_fields
|
raw_fields
|
||||||
|> Enum.filter(fn %{"name" => n} -> n != "" end)
|
|> Enum.filter(fn %{"name" => n} -> n != "" end)
|
||||||
|
|> Enum.map(fn field ->
|
||||||
|
previous =
|
||||||
|
old_fields
|
||||||
|
|> Enum.find(fn %{"value" => value} -> field["value"] == value end)
|
||||||
|
|
||||||
|
if previous && Map.has_key?(previous, "verified_at") do
|
||||||
|
field
|
||||||
|
|> Map.put("verified_at", previous["verified_at"])
|
||||||
|
else
|
||||||
|
field
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
fields =
|
fields =
|
||||||
raw_fields
|
raw_fields
|
||||||
|
@ -1200,6 +1215,10 @@ def update_and_set_cache(struct, params) do
|
||||||
|
|
||||||
def update_and_set_cache(changeset) do
|
def update_and_set_cache(changeset) do
|
||||||
with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
|
with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
|
||||||
|
if get_change(changeset, :raw_fields) do
|
||||||
|
BackgroundWorker.enqueue("verify_fields_links", %{"user_id" => user.id})
|
||||||
|
end
|
||||||
|
|
||||||
set_cache(user)
|
set_cache(user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1975,8 +1994,45 @@ def perform(:delete, %User{} = user) do
|
||||||
maybe_delete_from_db(user)
|
maybe_delete_from_db(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def perform(:verify_fields_links, user) do
|
||||||
|
profile_urls = [user.ap_id]
|
||||||
|
|
||||||
|
fields =
|
||||||
|
user.raw_fields
|
||||||
|
|> Enum.map(&verify_field_link(&1, profile_urls))
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
user
|
||||||
|
|> update_changeset(%{raw_fields: fields})
|
||||||
|
|
||||||
|
with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
|
||||||
|
set_cache(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def perform(:set_activation_async, user, status), do: set_activation(user, status)
|
def perform(:set_activation_async, user, status), do: set_activation(user, status)
|
||||||
|
|
||||||
|
defp verify_field_link(field, profile_urls) do
|
||||||
|
verified_at =
|
||||||
|
with %{"value" => value} <- field,
|
||||||
|
{:verified_at, nil} <- {:verified_at, Map.get(field, "verified_at")},
|
||||||
|
%{scheme: scheme, userinfo: nil, host: host}
|
||||||
|
when not_empty_string(host) and scheme in ["http", "https"] <-
|
||||||
|
URI.parse(value),
|
||||||
|
{:not_idn, true} <- {:not_idn, to_string(:idna.encode(host)) == host},
|
||||||
|
"me" <- Pleroma.Web.RelMe.maybe_put_rel_me(value, profile_urls) do
|
||||||
|
CommonUtils.to_masto_date(NaiveDateTime.utc_now())
|
||||||
|
else
|
||||||
|
{:verified_at, value} when not_empty_string(value) ->
|
||||||
|
value
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
Map.put(field, "verified_at", verified_at)
|
||||||
|
end
|
||||||
|
|
||||||
@spec external_users_query() :: Ecto.Query.t()
|
@spec external_users_query() :: Ecto.Query.t()
|
||||||
def external_users_query do
|
def external_users_query do
|
||||||
User.Query.build(%{
|
User.Query.build(%{
|
||||||
|
@ -2664,10 +2720,11 @@ def sanitize_html(%User{} = user) do
|
||||||
# - display name
|
# - display name
|
||||||
def sanitize_html(%User{} = user, filter) do
|
def sanitize_html(%User{} = user, filter) do
|
||||||
fields =
|
fields =
|
||||||
Enum.map(user.fields, fn %{"name" => name, "value" => value} ->
|
Enum.map(user.fields, fn %{"name" => name, "value" => value} = fields ->
|
||||||
%{
|
%{
|
||||||
"name" => name,
|
"name" => name,
|
||||||
"value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
"value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly),
|
||||||
|
"verified_at" => Map.get(fields, "verified_at")
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,14 @@ defp wait_backup(backup, current_processed, task) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
|
@files [
|
||||||
|
'actor.json',
|
||||||
|
'outbox.json',
|
||||||
|
'likes.json',
|
||||||
|
'bookmarks.json',
|
||||||
|
'followers.json',
|
||||||
|
'following.json'
|
||||||
|
]
|
||||||
@spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error
|
@spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error
|
||||||
def export(%__MODULE__{} = backup, caller_pid) do
|
def export(%__MODULE__{} = backup, caller_pid) do
|
||||||
backup = Repo.preload(backup, :user)
|
backup = Repo.preload(backup, :user)
|
||||||
|
@ -207,6 +214,8 @@ def export(%__MODULE__{} = backup, caller_pid) do
|
||||||
:ok <- statuses(dir, backup.user, caller_pid),
|
:ok <- statuses(dir, backup.user, caller_pid),
|
||||||
:ok <- likes(dir, backup.user, caller_pid),
|
:ok <- likes(dir, backup.user, caller_pid),
|
||||||
:ok <- bookmarks(dir, backup.user, caller_pid),
|
:ok <- bookmarks(dir, backup.user, caller_pid),
|
||||||
|
:ok <- followers(dir, backup.user, caller_pid),
|
||||||
|
:ok <- following(dir, backup.user, caller_pid),
|
||||||
{:ok, zip_path} <- :zip.create(backup.file_name, @files, cwd: dir),
|
{:ok, zip_path} <- :zip.create(backup.file_name, @files, cwd: dir),
|
||||||
{:ok, _} <- File.rm_rf(dir) do
|
{:ok, _} <- File.rm_rf(dir) do
|
||||||
{:ok, zip_path}
|
{:ok, zip_path}
|
||||||
|
@ -357,6 +366,16 @@ defp statuses(dir, user, caller_pid) do
|
||||||
caller_pid
|
caller_pid
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp followers(dir, user, caller_pid) do
|
||||||
|
User.get_followers_query(user)
|
||||||
|
|> write(dir, "followers", fn a -> {:ok, a.ap_id} end, caller_pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp following(dir, user, caller_pid) do
|
||||||
|
User.get_friends_query(user)
|
||||||
|
|> write(dir, "following", fn a -> {:ok, a.ap_id} end, caller_pid)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule Pleroma.User.Backup.ProcessorAPI do
|
defmodule Pleroma.User.Backup.ProcessorAPI do
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.ForceMention do
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||||
|
|
||||||
|
defp get_author(url) do
|
||||||
|
with %Object{data: %{"actor" => actor}} <- Object.normalize(url, fetch: false),
|
||||||
|
%User{ap_id: ap_id, nickname: nickname} <- User.get_cached_by_ap_id(actor) do
|
||||||
|
%{"type" => "Mention", "href" => ap_id, "name" => "@#{nickname}"}
|
||||||
|
else
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepend_author(tags, _, false), do: tags
|
||||||
|
|
||||||
|
defp prepend_author(tags, nil, _), do: tags
|
||||||
|
|
||||||
|
defp prepend_author(tags, url, _) do
|
||||||
|
actor = get_author(url)
|
||||||
|
|
||||||
|
if not is_nil(actor) do
|
||||||
|
[actor | tags]
|
||||||
|
else
|
||||||
|
tags
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(%{"type" => "Create", "object" => %{"tag" => tag} = object} = activity) do
|
||||||
|
tag =
|
||||||
|
tag
|
||||||
|
|> prepend_author(
|
||||||
|
object["inReplyTo"],
|
||||||
|
Config.get([:mrf_force_mention, :mention_parent, true])
|
||||||
|
)
|
||||||
|
|> prepend_author(
|
||||||
|
object["quoteUrl"],
|
||||||
|
Config.get([:mrf_force_mention, :mention_quoted, true])
|
||||||
|
)
|
||||||
|
|> Enum.uniq()
|
||||||
|
|
||||||
|
{:ok, put_in(activity["object"]["tag"], tag)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(object), do: {:ok, object}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def describe, do: {:ok, %{}}
|
||||||
|
end
|
|
@ -28,6 +28,7 @@ def render("show.json", _) do
|
||||||
|> to_string,
|
|> to_string,
|
||||||
registrations: Keyword.get(instance, :registrations_open),
|
registrations: Keyword.get(instance, :registrations_open),
|
||||||
approval_required: Keyword.get(instance, :account_approval_required),
|
approval_required: Keyword.get(instance, :account_approval_required),
|
||||||
|
contact_account: contact_account(Keyword.get(instance, :contact_username)),
|
||||||
configuration: configuration(),
|
configuration: configuration(),
|
||||||
# Extra (not present in Mastodon):
|
# Extra (not present in Mastodon):
|
||||||
max_toot_chars: Keyword.get(instance, :limit),
|
max_toot_chars: Keyword.get(instance, :limit),
|
||||||
|
@ -63,11 +64,12 @@ def render("show2.json", _) do
|
||||||
registrations: %{
|
registrations: %{
|
||||||
enabled: Keyword.get(instance, :registrations_open),
|
enabled: Keyword.get(instance, :registrations_open),
|
||||||
approval_required: Keyword.get(instance, :account_approval_required),
|
approval_required: Keyword.get(instance, :account_approval_required),
|
||||||
message: nil
|
message: nil,
|
||||||
|
url: nil
|
||||||
},
|
},
|
||||||
contact: %{
|
contact: %{
|
||||||
email: Keyword.get(instance, :email),
|
email: Keyword.get(instance, :email),
|
||||||
account: nil
|
account: contact_account(Keyword.get(instance, :contact_username))
|
||||||
},
|
},
|
||||||
# Extra (not present in Mastodon):
|
# Extra (not present in Mastodon):
|
||||||
pleroma: pleroma_configuration2(instance)
|
pleroma: pleroma_configuration2(instance)
|
||||||
|
@ -78,7 +80,8 @@ defp common_information(instance) do
|
||||||
%{
|
%{
|
||||||
title: Keyword.get(instance, :name),
|
title: Keyword.get(instance, :name),
|
||||||
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
|
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
|
||||||
languages: Keyword.get(instance, :languages, ["en"])
|
languages: Keyword.get(instance, :languages, ["en"]),
|
||||||
|
rules: []
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -168,15 +171,35 @@ defp fields_limits do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp contact_account(nil), do: nil
|
||||||
|
|
||||||
|
defp contact_account("@" <> username) do
|
||||||
|
contact_account(username)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp contact_account(username) do
|
||||||
|
user = Pleroma.User.get_cached_by_nickname(username)
|
||||||
|
|
||||||
|
if user do
|
||||||
|
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user, for: nil})
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp configuration do
|
defp configuration do
|
||||||
%{
|
%{
|
||||||
|
accounts: %{
|
||||||
|
max_featured_tags: 0
|
||||||
|
},
|
||||||
statuses: %{
|
statuses: %{
|
||||||
max_characters: Config.get([:instance, :limit]),
|
max_characters: Config.get([:instance, :limit]),
|
||||||
max_media_attachments: Config.get([:instance, :max_media_attachments])
|
max_media_attachments: Config.get([:instance, :max_media_attachments])
|
||||||
},
|
},
|
||||||
media_attachments: %{
|
media_attachments: %{
|
||||||
image_size_limit: Config.get([:instance, :upload_limit]),
|
image_size_limit: Config.get([:instance, :upload_limit]),
|
||||||
video_size_limit: Config.get([:instance, :upload_limit])
|
video_size_limit: Config.get([:instance, :upload_limit]),
|
||||||
|
supported_mime_types: ["application/octet-stream"]
|
||||||
},
|
},
|
||||||
polls: %{
|
polls: %{
|
||||||
max_options: Config.get([:instance, :poll_limits, :max_options]),
|
max_options: Config.get([:instance, :poll_limits, :max_options]),
|
||||||
|
@ -190,7 +213,13 @@ defp configuration do
|
||||||
defp configuration2 do
|
defp configuration2 do
|
||||||
configuration()
|
configuration()
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
urls: %{streaming: Pleroma.Web.Endpoint.websocket_url()}
|
urls: %{
|
||||||
|
streaming: Pleroma.Web.Endpoint.websocket_url(),
|
||||||
|
status: Config.get([:instance, :status_page])
|
||||||
|
},
|
||||||
|
vapid: %{
|
||||||
|
public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,11 @@ def perform(%Job{
|
||||||
Pleroma.FollowingRelationship.move_following(origin, target)
|
Pleroma.FollowingRelationship.move_following(origin, target)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def perform(%Job{args: %{"op" => "verify_fields_links", "user_id" => user_id}}) do
|
||||||
|
user = User.get_by_id(user_id)
|
||||||
|
User.perform(:verify_fields_links, user)
|
||||||
|
end
|
||||||
|
|
||||||
def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do
|
def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do
|
||||||
Instance.perform(:delete_instance, host)
|
Instance.perform(:delete_instance, host)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"@context":"https://www.w3.org/ns/activitystreams","type":"Note","id":"https://www.minds.com/api/activitypub/users/1198929502760083472/entities/urn:comment:1600926863310458883:0:0:0:1600932467852709903","attributedTo":"https://www.minds.com/api/activitypub/users/1198929502760083472","content":"\u003Ca class=\u0022u-url mention\u0022 href=\u0022https://www.minds.com/lain\u0022 target=\u0022_blank\u0022\u003E@lain\u003C/a\u003E corn syrup.","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://www.minds.com/api/activitypub/users/1198929502760083472/followers","https://lain.com/users/lain"],"tag":[{"type":"Mention","href":"https://www.minds.com/api/activitypub/users/464237775479123984","name":"@lain"}],"url":"https://www.minds.com/newsfeed/1600926863310458883?focusedCommentUrn=urn:comment:1600926863310458883:0:0:0:1600932467852709903","published":"2024-02-04T17:34:03+00:00","inReplyTo":"https://lain.com/objects/36254095-c839-4167-bcc2-b361d5de9198","source":{"content":"@lain corn syrup.","mediaType":"text/plain"}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"@context":["https://www.w3.org/ns/activitystreams","https://lain.com/schemas/litepub-0.1.jsonld",{"@language":"und"}],"actor":"https://lain.com/users/lain","attachment":[],"attributedTo":"https://lain.com/users/lain","cc":["https://lain.com/users/lain/followers"],"content":"which diet is the best for cognitive dissonance","context":"https://lain.com/contexts/98c8a130-e813-4797-8973-600e80114317","conversation":"https://lain.com/contexts/98c8a130-e813-4797-8973-600e80114317","id":"https://lain.com/objects/36254095-c839-4167-bcc2-b361d5de9198","published":"2024-02-04T17:11:23.931890Z","repliesCount":11,"sensitive":null,"source":{"content":"which diet is the best for cognitive dissonance","mediaType":"text/plain"},"summary":"","tag":[],"to":["https://www.w3.org/ns/activitystreams#Public"],"type":"Note"}
|
|
@ -166,6 +166,7 @@ test "it removes outdated backups after creating a fresh one" do
|
||||||
|
|
||||||
test "it creates a zip archive with user data" do
|
test "it creates a zip archive with user data" do
|
||||||
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
|
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
|
||||||
|
%{ap_id: other_ap_id} = other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, %{object: %{data: %{"id" => id1}}} = status1} =
|
{:ok, %{object: %{data: %{"id" => id1}}} = status1} =
|
||||||
CommonAPI.post(user, %{status: "status1"})
|
CommonAPI.post(user, %{status: "status1"})
|
||||||
|
@ -182,6 +183,8 @@ test "it creates a zip archive with user data" do
|
||||||
Bookmark.create(user.id, status2.id)
|
Bookmark.create(user.id, status2.id)
|
||||||
Bookmark.create(user.id, status3.id)
|
Bookmark.create(user.id, status3.id)
|
||||||
|
|
||||||
|
CommonAPI.follow(user, other_user)
|
||||||
|
|
||||||
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
|
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
|
||||||
assert {:ok, path} = Backup.export(backup, self())
|
assert {:ok, path} = Backup.export(backup, self())
|
||||||
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory])
|
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory])
|
||||||
|
@ -261,6 +264,16 @@ test "it creates a zip archive with user data" do
|
||||||
"type" => "OrderedCollection"
|
"type" => "OrderedCollection"
|
||||||
} = Jason.decode!(json)
|
} = Jason.decode!(json)
|
||||||
|
|
||||||
|
assert {:ok, {'following.json', json}} = :zip.zip_get('following.json', zipfile)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "following.json",
|
||||||
|
"orderedItems" => [^other_ap_id],
|
||||||
|
"totalItems" => 1,
|
||||||
|
"type" => "OrderedCollection"
|
||||||
|
} = Jason.decode!(json)
|
||||||
|
|
||||||
:zip.zip_close(zipfile)
|
:zip.zip_close(zipfile)
|
||||||
File.rm!(path)
|
File.rm!(path)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2928,4 +2928,51 @@ test "it doesn't pin users you do not follow" do
|
||||||
refute User.endorses?(user, pinned_user)
|
refute User.endorses?(user, pinned_user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it checks fields links for a backlink" do
|
||||||
|
user = insert(:user, ap_id: "https://social.example.org/users/lain")
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
%{"name" => "Link", "value" => "http://example.com/rel_me/null"},
|
||||||
|
%{"name" => "Verified link", "value" => "http://example.com/rel_me/link"},
|
||||||
|
%{"name" => "Not a link", "value" => "i'm not a link"}
|
||||||
|
]
|
||||||
|
|
||||||
|
user
|
||||||
|
|> User.update_and_set_cache(%{raw_fields: fields})
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{"verified_at" => nil},
|
||||||
|
%{"verified_at" => verified_at},
|
||||||
|
%{"verified_at" => nil}
|
||||||
|
] = user.fields
|
||||||
|
|
||||||
|
assert is_binary(verified_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updating fields does not invalidate previously validated links" do
|
||||||
|
user = insert(:user, ap_id: "https://social.example.org/users/lain")
|
||||||
|
|
||||||
|
user
|
||||||
|
|> User.update_and_set_cache(%{
|
||||||
|
raw_fields: [%{"name" => "verified link", "value" => "http://example.com/rel_me/link"}]
|
||||||
|
})
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
%User{fields: [%{"verified_at" => verified_at}]} = user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
|
user
|
||||||
|
|> User.update_and_set_cache(%{
|
||||||
|
raw_fields: [%{"name" => "Verified link", "value" => "http://example.com/rel_me/link"}]
|
||||||
|
})
|
||||||
|
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
|
assert [%{"verified_at" => ^verified_at}] = user.fields
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF.ForceMention
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "adds mention to a reply" do
|
||||||
|
lain =
|
||||||
|
insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain@lain.com", local: false)
|
||||||
|
|
||||||
|
niobleoum =
|
||||||
|
insert(:user,
|
||||||
|
ap_id: "https://www.minds.com/api/activitypub/users/1198929502760083472",
|
||||||
|
nickname: "niobleoum@minds.com",
|
||||||
|
local: false
|
||||||
|
)
|
||||||
|
|
||||||
|
status = File.read!("test/fixtures/minds-pleroma-mentioned-post.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
status_activity = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => lain.ap_id,
|
||||||
|
"object" => status
|
||||||
|
}
|
||||||
|
|
||||||
|
Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(status_activity)
|
||||||
|
|
||||||
|
reply = File.read!("test/fixtures/minds-invalid-mention-post.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
reply_activity = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => niobleoum.ap_id,
|
||||||
|
"object" => reply
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %{"object" => %{"tag" => tag}}} = ForceMention.filter(reply_activity)
|
||||||
|
|
||||||
|
assert Enum.find(tag, fn %{"href" => href} -> href == lain.ap_id end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "adds mention to a quote" do
|
||||||
|
user1 = insert(:user, ap_id: "https://misskey.io/users/83ssedkv53")
|
||||||
|
user2 = insert(:user, ap_id: "https://misskey.io/users/7rkrarq81i")
|
||||||
|
|
||||||
|
status = File.read!("test/fixtures/tesla_mock/misskey.io_8vs6wxufd0.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
status_activity = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => user1.ap_id,
|
||||||
|
"object" => status
|
||||||
|
}
|
||||||
|
|
||||||
|
Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(status_activity)
|
||||||
|
|
||||||
|
quote_post = File.read!("test/fixtures/quote_post/misskey_quote_post.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
quote_activity = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => user2.ap_id,
|
||||||
|
"object" => quote_post
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %{"object" => %{"tag" => tag}}} = ForceMention.filter(quote_activity)
|
||||||
|
|
||||||
|
assert Enum.find(tag, fn %{"href" => href} -> href == user1.ap_id end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -107,6 +107,18 @@ test "instance languages", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "get instance contact information", %{conn: conn} do
|
||||||
|
user = insert(:user, %{local: true})
|
||||||
|
|
||||||
|
clear_config([:instance, :contact_username], user.nickname)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
|
||||||
|
assert result = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
assert result["contact_account"]["id"] == user.id
|
||||||
|
end
|
||||||
|
|
||||||
test "get instance information v2", %{conn: conn} do
|
test "get instance information v2", %{conn: conn} do
|
||||||
clear_config([:auth, :oauth_consumer_strategies], [])
|
clear_config([:auth, :oauth_consumer_strategies], [])
|
||||||
|
|
||||||
|
|
|
@ -511,10 +511,15 @@ test "update fields", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert account_data["fields"] == [
|
assert account_data["fields"] == [
|
||||||
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
|
%{
|
||||||
|
"name" => "<a href=\"http://google.com\">foo</a>",
|
||||||
|
"value" => "bar",
|
||||||
|
"verified_at" => nil
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
"name" => "link.io",
|
"name" => "link.io",
|
||||||
"value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)
|
"value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>),
|
||||||
|
"verified_at" => nil
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -573,8 +578,8 @@ test "emojis in fields labels", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert account_data["fields"] == [
|
assert account_data["fields"] == [
|
||||||
%{"name" => ":firefox:", "value" => "is best 2hu"},
|
%{"name" => ":firefox:", "value" => "is best 2hu", "verified_at" => nil},
|
||||||
%{"name" => "they wins", "value" => ":blank:"}
|
%{"name" => "they wins", "value" => ":blank:", "verified_at" => nil}
|
||||||
]
|
]
|
||||||
|
|
||||||
assert account_data["source"]["fields"] == [
|
assert account_data["source"]["fields"] == [
|
||||||
|
@ -602,10 +607,11 @@ test "update fields via x-www-form-urlencoded", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert account["fields"] == [
|
assert account["fields"] == [
|
||||||
%{"name" => "foo", "value" => "bar"},
|
%{"name" => "foo", "value" => "bar", "verified_at" => nil},
|
||||||
%{
|
%{
|
||||||
"name" => "link",
|
"name" => "link",
|
||||||
"value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>)
|
"value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>),
|
||||||
|
"verified_at" => nil
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -627,7 +633,7 @@ test "update fields with empty name", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert account["fields"] == [
|
assert account["fields"] == [
|
||||||
%{"name" => "foo", "value" => ""}
|
%{"name" => "foo", "value" => "", "verified_at" => nil}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue