Merge branch 'develop' into 'new-user-emails'

This commit is contained in:
feld 2021-02-08 22:01:32 +00:00 committed by Mark Felder
commit 593c0851d9
15 changed files with 475 additions and 149 deletions

View file

@ -19,6 +19,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Improved Apache webserver support: updated sample configuration, MediaProxy cache invalidation verified with the included sample script - Improved Apache webserver support: updated sample configuration, MediaProxy cache invalidation verified with the included sample script
- Improve OAuth 2.0 provider support. A missing `fqn` field was added to the response, but does not expose the user's email address. - Improve OAuth 2.0 provider support. A missing `fqn` field was added to the response, but does not expose the user's email address.
- Provide redirect of external posts from `/notice/:id` to their original URL - Provide redirect of external posts from `/notice/:id` to their original URL
- Admins no longer receive notifications for reports if they are the actor making the report.
- Improved Mailer configuration setting descriptions for AdminFE.
<details> <details>
<summary>API Changes</summary> <summary>API Changes</summary>
@ -49,6 +51,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Ability to set ActivityPub aliases for follower migration. - Ability to set ActivityPub aliases for follower migration.
- Configurable background job limits for RichMedia (link previews) and MediaProxyWarmingPolicy - Configurable background job limits for RichMedia (link previews) and MediaProxyWarmingPolicy
- Ability to define custom HTTP headers per each frontend - Ability to define custom HTTP headers per each frontend
- MRF (`NoEmptyPolicy`): New MRF Policy which will deny empty statuses or statuses of only mentions from being created by local users
- New users will receive a simple email confirming their registration if no other emails will be dispatched. (e.g., Welcome, Confirmation, or Approval Required) - New users will receive a simple email confirming their registration if no other emails will be dispatched. (e.g., Welcome, Confirmation, or Approval Required)
<details> <details>

View file

@ -99,7 +99,8 @@
key: :base_url, key: :base_url,
label: "Base URL", label: "Base URL",
type: :string, type: :string,
description: "Base URL for the uploads, needed if you use CDN", description:
"Base URL for the uploads. Required if you use a CDN or host attachments under a different domain.",
suggestions: [ suggestions: [
"https://cdn-host.com" "https://cdn-host.com"
] ]
@ -214,253 +215,216 @@
type: :group, type: :group,
description: "Mailer-related settings", description: "Mailer-related settings",
children: [ children: [
%{
key: :enabled,
label: "Mailer Enabled",
type: :boolean
},
%{ %{
key: :adapter, key: :adapter,
type: :module, type: :module,
description: description:
"One of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters)," <> "One of the mail adapters listed in [Swoosh documentation](https://hexdocs.pm/swoosh/Swoosh.html#module-adapters)",
" or Swoosh.Adapters.Local for in-memory mailbox",
suggestions: [ suggestions: [
Swoosh.Adapters.AmazonSES,
Swoosh.Adapters.Dyn,
Swoosh.Adapters.Gmail,
Swoosh.Adapters.Mailgun,
Swoosh.Adapters.Mailjet,
Swoosh.Adapters.Mandrill,
Swoosh.Adapters.Postmark,
Swoosh.Adapters.SMTP, Swoosh.Adapters.SMTP,
Swoosh.Adapters.Sendgrid, Swoosh.Adapters.Sendgrid,
Swoosh.Adapters.Sendmail, Swoosh.Adapters.Sendmail,
Swoosh.Adapters.Mandrill,
Swoosh.Adapters.Mailgun,
Swoosh.Adapters.Mailjet,
Swoosh.Adapters.Postmark,
Swoosh.Adapters.SparkPost,
Swoosh.Adapters.AmazonSES,
Swoosh.Adapters.Dyn,
Swoosh.Adapters.SocketLabs, Swoosh.Adapters.SocketLabs,
Swoosh.Adapters.Gmail, Swoosh.Adapters.SparkPost
Swoosh.Adapters.Local
] ]
}, },
%{
key: :enabled,
type: :boolean,
description: "Allow/disallow send emails"
},
%{ %{
group: {:subgroup, Swoosh.Adapters.SMTP}, group: {:subgroup, Swoosh.Adapters.SMTP},
key: :relay, key: :relay,
type: :string, type: :string,
description: "`Swoosh.Adapters.SMTP` adapter specific setting", description: "Hostname or IP address",
suggestions: ["smtp.gmail.com"] suggestions: ["smtp.example.com"]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :username,
type: :string,
description: "`Swoosh.Adapters.SMTP` adapter specific setting",
suggestions: ["pleroma"]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :password,
type: :string,
description: "`Swoosh.Adapters.SMTP` adapter specific setting",
suggestions: ["password"]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :ssl,
label: "SSL",
type: :boolean,
description: "`Swoosh.Adapters.SMTP` adapter specific setting"
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :tls,
label: "TLS",
type: :atom,
description: "`Swoosh.Adapters.SMTP` adapter specific setting",
suggestions: [:always, :never, :if_available]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :auth,
type: :atom,
description: "`Swoosh.Adapters.SMTP` adapter specific setting",
suggestions: [:always, :never, :if_available]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.SMTP}, group: {:subgroup, Swoosh.Adapters.SMTP},
key: :port, key: :port,
type: :integer, type: :integer,
description: "`Swoosh.Adapters.SMTP` adapter specific setting", description: "SMTP port",
suggestions: [1025] suggestions: ["1025"]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :username,
type: :string,
description: "SMTP AUTH username",
suggestions: ["user@example.com"]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :password,
type: :string,
description: "SMTP AUTH password",
suggestions: ["password"]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :ssl,
label: "Use SSL",
type: :boolean,
description: "Use Implicit SSL/TLS. e.g. port 465"
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :tls,
label: "STARTTLS Mode",
type: {:dropdown, :atom},
description: "Explicit TLS (STARTTLS) enforcement mode",
suggestions: [:if_available, :always, :never]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :auth,
label: "AUTH Mode",
type: {:dropdown, :atom},
description: "SMTP AUTH enforcement mode",
suggestions: [:if_available, :always, :never]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.SMTP}, group: {:subgroup, Swoosh.Adapters.SMTP},
key: :retries, key: :retries,
type: :integer, type: :integer,
description: "`Swoosh.Adapters.SMTP` adapter specific setting", description: "SMTP temporary (4xx) error retries",
suggestions: [5] suggestions: [1]
},
%{
group: {:subgroup, Swoosh.Adapters.SMTP},
key: :no_mx_lookups,
label: "No MX lookups",
type: :boolean,
description: "`Swoosh.Adapters.SMTP` adapter specific setting"
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Sendgrid}, group: {:subgroup, Swoosh.Adapters.Sendgrid},
key: :api_key, key: :api_key,
label: "API key", label: "SendGrid API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.Sendgrid` adapter specific setting", suggestions: ["YOUR_API_KEY"]
suggestions: ["my-api-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Sendmail}, group: {:subgroup, Swoosh.Adapters.Sendmail},
key: :cmd_path, key: :cmd_path,
type: :string, type: :string,
description: "`Swoosh.Adapters.Sendmail` adapter specific setting",
suggestions: ["/usr/bin/sendmail"] suggestions: ["/usr/bin/sendmail"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Sendmail}, group: {:subgroup, Swoosh.Adapters.Sendmail},
key: :cmd_args, key: :cmd_args,
type: :string, type: :string,
description: "`Swoosh.Adapters.Sendmail` adapter specific setting",
suggestions: ["-N delay,failure,success"] suggestions: ["-N delay,failure,success"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Sendmail}, group: {:subgroup, Swoosh.Adapters.Sendmail},
key: :qmail, key: :qmail,
type: :boolean, label: "Qmail compat mode",
description: "`Swoosh.Adapters.Sendmail` adapter specific setting" type: :boolean
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Mandrill}, group: {:subgroup, Swoosh.Adapters.Mandrill},
key: :api_key, key: :api_key,
label: "API key", label: "Mandrill API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.Mandrill` adapter specific setting", suggestions: ["YOUR_API_KEY"]
suggestions: ["my-api-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Mailgun}, group: {:subgroup, Swoosh.Adapters.Mailgun},
key: :api_key, key: :api_key,
label: "API key", label: "Mailgun API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.Mailgun` adapter specific setting", suggestions: ["YOUR_API_KEY"]
suggestions: ["my-api-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Mailgun}, group: {:subgroup, Swoosh.Adapters.Mailgun},
key: :domain, key: :domain,
type: :string, type: :string,
description: "`Swoosh.Adapters.Mailgun` adapter specific setting", suggestions: ["YOUR_DOMAIN_NAME"]
suggestions: ["pleroma.com"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Mailjet}, group: {:subgroup, Swoosh.Adapters.Mailjet},
key: :api_key, key: :api_key,
label: "API key", label: "MailJet Public API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.Mailjet` adapter specific setting", suggestions: ["MJ_APIKEY_PUBLIC"]
suggestions: ["my-api-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Mailjet}, group: {:subgroup, Swoosh.Adapters.Mailjet},
key: :secret, key: :secret,
label: "MailJet Private API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.Mailjet` adapter specific setting", suggestions: ["MJ_APIKEY_PRIVATE"]
suggestions: ["my-secret-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Postmark}, group: {:subgroup, Swoosh.Adapters.Postmark},
key: :api_key, key: :api_key,
label: "API key", label: "Postmark API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.Postmark` adapter specific setting", suggestions: ["X-Postmark-Server-Token"]
suggestions: ["my-api-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.SparkPost}, group: {:subgroup, Swoosh.Adapters.SparkPost},
key: :api_key, key: :api_key,
label: "API key", label: "SparkPost API key",
type: :string, type: :string,
description: "`Swoosh.Adapters.SparkPost` adapter specific setting", suggestions: ["YOUR_API_KEY"]
suggestions: ["my-api-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.SparkPost}, group: {:subgroup, Swoosh.Adapters.SparkPost},
key: :endpoint, key: :endpoint,
type: :string, type: :string,
description: "`Swoosh.Adapters.SparkPost` adapter specific setting",
suggestions: ["https://api.sparkpost.com/api/v1"] suggestions: ["https://api.sparkpost.com/api/v1"]
}, },
%{
group: {:subgroup, Swoosh.Adapters.AmazonSES},
key: :region,
type: :string,
description: "`Swoosh.Adapters.AmazonSES` adapter specific setting",
suggestions: ["us-east-1", "us-east-2"]
},
%{ %{
group: {:subgroup, Swoosh.Adapters.AmazonSES}, group: {:subgroup, Swoosh.Adapters.AmazonSES},
key: :access_key, key: :access_key,
label: "AWS Access Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", suggestions: ["AWS_ACCESS_KEY"]
suggestions: ["aws-access-key"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.AmazonSES}, group: {:subgroup, Swoosh.Adapters.AmazonSES},
key: :secret, key: :secret,
label: "AWS Secret Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", suggestions: ["AWS_SECRET_KEY"]
suggestions: ["aws-secret-key"] },
%{
group: {:subgroup, Swoosh.Adapters.AmazonSES},
key: :region,
label: "AWS Region",
type: :string,
suggestions: ["us-east-1", "us-east-2"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Dyn}, group: {:subgroup, Swoosh.Adapters.Dyn},
key: :api_key, key: :api_key,
label: "API key", label: "Dyn API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.Dyn` adapter specific setting", suggestions: ["apikey"]
suggestions: ["my-api-key"]
},
%{
group: {:subgroup, Swoosh.Adapters.SocketLabs},
key: :server_id,
type: :string,
description: "`Swoosh.Adapters.SocketLabs` adapter specific setting"
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.SocketLabs}, group: {:subgroup, Swoosh.Adapters.SocketLabs},
key: :api_key, key: :api_key,
label: "API key", label: "SocketLabs API Key",
type: :string, type: :string,
description: "`Swoosh.Adapters.SocketLabs` adapter specific setting" suggestions: ["INJECTION_API_KEY"]
},
%{
group: {:subgroup, Swoosh.Adapters.SocketLabs},
key: :server_id,
label: "Server ID",
type: :string,
suggestions: ["SERVER_ID"]
}, },
%{ %{
group: {:subgroup, Swoosh.Adapters.Gmail}, group: {:subgroup, Swoosh.Adapters.Gmail},
key: :access_token, key: :access_token,
label: "GMail API Access Token",
type: :string, type: :string,
description: "`Swoosh.Adapters.Gmail` adapter specific setting" suggestions: ["GMAIL_API_ACCESS_TOKEN"]
}
]
},
%{
group: :swoosh,
type: :group,
description: "`Swoosh.Adapters.Local` adapter specific settings",
children: [
%{
group: {:subgroup, Swoosh.Adapters.Local},
key: :serve_mailbox,
type: :boolean,
description: "Run the preview server together as part of your app"
},
%{
group: {:subgroup, Swoosh.Adapters.Local},
key: :preview_port,
type: :integer,
description: "The preview server port",
suggestions: [4001]
} }
] ]
}, },
@ -1545,7 +1509,8 @@
%{ %{
key: :max_body_length, key: :max_body_length,
type: :integer, type: :integer,
description: "Maximum file size allowed through the Pleroma MediaProxy cache." description:
"Maximum file size (in bytes) allowed through the Pleroma MediaProxy cache."
}, },
%{ %{
key: :max_read_duration, key: :max_read_duration,
@ -1595,7 +1560,7 @@
key: :min_content_length, key: :min_content_length,
type: :integer, type: :integer,
description: description:
"Min content length to perform preview, in bytes. If greater than 0, media smaller in size will be served as is, without thumbnailing." "Min content length (in bytes) to perform preview. Media smaller in size will be served without thumbnailing."
} }
] ]
}, },
@ -1643,6 +1608,7 @@
}, },
%{ %{
key: :url_format, key: :url_format,
label: "URL Format",
type: :string, type: :string,
description: description:
"Optional URL format preprocessing. Only required for Apache's htcacheclean.", "Optional URL format preprocessing. Only required for Apache's htcacheclean.",
@ -2888,7 +2854,7 @@
type: :integer, type: :integer,
description: description:
"Activity pub routes (except question activities). Default: `nil` (no expiration).", "Activity pub routes (except question activities). Default: `nil` (no expiration).",
suggestions: [30_000, nil] suggestions: [nil]
}, },
%{ %{
key: :activity_pub_question, key: :activity_pub_question,
@ -3326,9 +3292,9 @@
}, },
%{ %{
key: :ip_whitelist, key: :ip_whitelist,
label: "IP Whitelist",
type: [{:list, :string}, {:list, :charlist}, {:list, :tuple}], type: [{:list, :string}, {:list, :charlist}, {:list, :tuple}],
description: description: "Restrict access of app metrics endpoint to the specified IP addresses."
"[Pleroma extension] If non-empty, restricts access to app metrics endpoint to specified IP addresses."
}, },
%{ %{
key: :auth, key: :auth,

View file

@ -141,3 +141,21 @@ but should only be run if necessary. **It is safe to cancel this.**
```sh ```sh
mix pleroma.database ensure_expiration mix pleroma.database ensure_expiration
``` ```
## Change Text Search Configuration
Change `default_text_search_config` for database and (if necessary) text_search_config used in index, then rebuild index (it may take time).
=== "OTP"
```sh
./bin/pleroma_ctl database set_text_search_config english
```
=== "From Source"
```sh
mix pleroma.database set_text_search_config english
```
See [PostgreSQL documentation](https://www.postgresql.org/docs/current/textsearch-configuration.html) and `docs/configuration/howto_search_cjk.md` for more detail.

View file

@ -0,0 +1,42 @@
# How to enable text search for Chinese, Japanese and Korean
Pleroma's full text search feature is powered by PostgreSQL's native [text search](https://www.postgresql.org/docs/current/textsearch.html), it works well out of box for most of languages, but needs extra configurations for some asian languages like Chinese, Japanese and Korean (CJK).
## Setup and test the new search config
In most cases, you would need an extension installed to support parsing CJK text. Here are a few extension you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community.
* [a generic n-gram parser](https://github.com/huangjimmy/pg_cjk_parser) supports Simplifed/Traditional Chinese, Japanese, and Korean
* [a Korean parser](https://github.com/i0seph/textsearch_ko) based on mecab
* [a Japanese parser](https://www.amris.co.jp/tsja/index.html) based on mecab
* [zhparser](https://github.com/amutu/zhparser/) is a PostgreSQL extension base on the Simple Chinese Word Segmentation(SCWS)
* [another Chinese parser](https://github.com/jaiminpan/pg_jieba) based on Jieba Chinese Word Segmentation
Once you have the new search config , make sure you test it with the `pleroma` user in PostgreSQL (change `YOUR.CONFIG` to your real configuration name)
```
SELECT ts_debug('YOUR.CONFIG', '安装和配置Nginx, ElixirとErlangをインストールします');
```
Check output of the query, and see if it matches your expectation.
## Update text search config and index in database
=== "OTP"
```sh
./bin/pleroma_ctl database set_text_search_config YOUR.CONFIG
```
=== "From Source"
```sh
mix pleroma.database set_text_search_config YOUR.CONFIG
```
Note: index update may take a while.
## Restart database connection
Since some changes above will only apply with a new database connection, you will have to restart either Pleroma or PostgreSQL process, or use `pg_terminate_backend` SQL command without restarting either.
Now the search results of statuses should be much more friendly for your language of choice, the results for searching users and tags were not changed, as the default parsing/matching should work for most cases.

View file

@ -59,6 +59,13 @@ sub vcl_backend_response {
set beresp.http.CR = beresp.http.content-range; set beresp.http.CR = beresp.http.content-range;
} }
# Bypass cache for large files
# 50000000 ~ 50MB
if (std.integer(beresp.http.content-length, 0) > 50000000) {
set beresp.uncacheable = true;
return(deliver);
}
# Don't cache objects that require authentication # Don't cache objects that require authentication
if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") { if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") {
set beresp.uncacheable = true; set beresp.uncacheable = true;

View file

@ -167,4 +167,51 @@ def run(["ensure_expiration"]) do
end) end)
|> Stream.run() |> Stream.run()
end end
def run(["set_text_search_config", tsconfig]) do
start_pleroma()
%{rows: [[tsc]]} = Ecto.Adapters.SQL.query!(Pleroma.Repo, "SHOW default_text_search_config;")
shell_info("Current default_text_search_config: #{tsc}")
%{rows: [[db]]} = Ecto.Adapters.SQL.query!(Pleroma.Repo, "SELECT current_database();")
shell_info("Update default_text_search_config: #{tsconfig}")
%{messages: msg} =
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"ALTER DATABASE #{db} SET default_text_search_config = '#{tsconfig}';"
)
# non-exist config will not raise excpetion but only give >0 messages
if length(msg) > 0 do
shell_info("Error: #{inspect(msg, pretty: true)}")
else
rum_enabled = Pleroma.Config.get([:database, :rum_enabled])
shell_info("Recreate index, RUM: #{rum_enabled}")
# Note SQL below needs to be kept up-to-date with latest GIN or RUM index definition in future
if rum_enabled do
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"CREATE OR REPLACE FUNCTION objects_fts_update() RETURNS trigger AS $$ BEGIN
new.fts_content := to_tsvector(new.data->>'content');
RETURN new;
END
$$ LANGUAGE plpgsql"
)
shell_info("Refresh RUM index")
Ecto.Adapters.SQL.query!(Pleroma.Repo, "UPDATE objects SET updated_at = NOW();")
else
Ecto.Adapters.SQL.query!(Pleroma.Repo, "DROP INDEX IF EXISTS objects_fts;")
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"CREATE INDEX objects_fts ON objects USING gin(to_tsvector('#{tsconfig}', data->>'content')); "
)
end
shell_info('Done.')
end
end
end end

View file

@ -64,7 +64,7 @@ defp query_with(q, :gin, search_query, :plain) do
from([a, o] in q, from([a, o] in q,
where: where:
fragment( fragment(
"to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)", "to_tsvector(?->>'content') @@ plainto_tsquery(?)",
o.data, o.data,
^search_query ^search_query
) )
@ -75,7 +75,7 @@ defp query_with(q, :gin, search_query, :websearch) do
from([a, o] in q, from([a, o] in q,
where: where:
fragment( fragment(
"to_tsvector('english', ?->>'content') @@ websearch_to_tsquery('english', ?)", "to_tsvector(?->>'content') @@ websearch_to_tsquery(?)",
o.data, o.data,
^search_query ^search_query
) )
@ -86,7 +86,7 @@ defp query_with(q, :rum, search_query, :plain) do
from([a, o] in q, from([a, o] in q,
where: where:
fragment( fragment(
"? @@ plainto_tsquery('english', ?)", "? @@ plainto_tsquery(?)",
o.fts_content, o.fts_content,
^search_query ^search_query
), ),
@ -98,7 +98,7 @@ defp query_with(q, :rum, search_query, :websearch) do
from([a, o] in q, from([a, o] in q,
where: where:
fragment( fragment(
"? @@ websearch_to_tsquery('english', ?)", "? @@ websearch_to_tsquery(?)",
o.fts_content, o.fts_content,
^search_query ^search_query
), ),

View file

@ -507,8 +507,8 @@ def get_potential_receiver_ap_ids(%{data: %{"type" => "Follow", "object" => obje
[object_id] [object_id]
end end
def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag"}}) do def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do
User.all_superusers() |> Enum.map(fn user -> user.ap_id end) (User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor]
end end
def get_potential_receiver_ap_ids(activity) do def get_potential_receiver_ap_ids(activity) do

View file

@ -377,6 +377,7 @@ defp do_flag(
:ok <- :ok <-
maybe_federate(stripped_activity) do maybe_federate(stripped_activity) do
User.all_superusers() User.all_superusers()
|> Enum.filter(fn user -> user.ap_id != actor end)
|> Enum.filter(fn user -> not is_nil(user.email) end) |> Enum.filter(fn user -> not is_nil(user.email) end)
|> Enum.each(fn superuser -> |> Enum.each(fn superuser ->
superuser superuser

View file

@ -0,0 +1,61 @@
# 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.MRF.NoEmptyPolicy do
@moduledoc "Filter local activities which have no content"
@behaviour Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web
@impl true
def filter(%{"actor" => actor} = object) do
with true <- is_local?(actor),
true <- is_note?(object),
false <- has_attachment?(object),
true <- only_mentions?(object) do
{:reject, "[NoEmptyPolicy]"}
else
_ ->
{:ok, object}
end
end
def filter(object), do: {:ok, object}
defp is_local?(actor) do
if actor |> String.starts_with?("#{Web.base_url()}") do
true
else
false
end
end
defp has_attachment?(%{
"type" => "Create",
"object" => %{"type" => "Note", "attachment" => attachments}
})
when length(attachments) > 0,
do: true
defp has_attachment?(_), do: false
defp only_mentions?(%{"type" => "Create", "object" => %{"type" => "Note", "source" => source}}) do
non_mentions =
source |> String.split() |> Enum.filter(&(not String.starts_with?(&1, "@"))) |> length
if non_mentions > 0 do
false
else
true
end
end
defp only_mentions?(_), do: false
defp is_note?(%{"type" => "Create", "object" => %{"type" => "Note"}}), do: true
defp is_note?(_), do: false
@impl true
def describe, do: {:ok, %{}}
end

View file

@ -9,7 +9,6 @@
<ostatus:conversation ref="<%= activity_context(@activity) %>"> <ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %> <%= activity_context(@activity) %>
</ostatus:conversation> </ostatus:conversation>
<link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
<%= if @data["summary"] do %> <%= if @data["summary"] do %>
<description><%= escape(@data["summary"]) %></description> <description><%= escape(@data["summary"]) %></description>
@ -21,6 +20,8 @@
<link><%= @data["external_url"] %></link> <link><%= @data["external_url"] %></link>
<% end %> <% end %>
<link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
<%= for tag <- @data["tag"] || [] do %> <%= for tag <- @data["tag"] || [] do %>
<category term="<%= tag %>"></category> <category term="<%= tag %>"></category>
<% end %> <% end %>

View file

@ -0,0 +1,11 @@
defmodule Pleroma.Repo.Migrations.AddDefaultTextSearchConfig do
use Ecto.Migration
def change do
execute("DO $$
BEGIN
execute 'ALTER DATABASE '||current_database()||' SET default_text_search_config = ''english'' ';
END
$$;")
end
end

View file

@ -17,7 +17,7 @@ def up do
execute("CREATE FUNCTION objects_fts_update() RETURNS trigger AS $$ execute("CREATE FUNCTION objects_fts_update() RETURNS trigger AS $$
begin begin
new.fts_content := to_tsvector('english', new.data->>'content'); new.fts_content := to_tsvector(new.data->>'content');
return new; return new;
end end
$$ LANGUAGE plpgsql") $$ LANGUAGE plpgsql")

View file

@ -45,6 +45,20 @@ test "creates a notification for a report" do
assert notification.type == "pleroma:report" assert notification.type == "pleroma:report"
end end
test "suppresses notification to reporter if reporter is an admin" do
reporting_admin = insert(:user, is_admin: true)
reported_user = insert(:user)
other_admin = insert(:user, is_admin: true)
{:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id})
{:ok, [notification]} = Notification.create_notifications(activity)
refute notification.user_id == reporting_admin.id
assert notification.user_id == other_admin.id
assert notification.type == "pleroma:report"
end
test "creates a notification for an emoji reaction" do test "creates a notification for an emoji reaction" do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)

View file

@ -0,0 +1,154 @@
# 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.MRF.NoEmptyPolicyTest do
use Pleroma.DataCase
alias Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy
setup_all do: clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy])
test "Notes with content are exempt" do
message = %{
"actor" => "http://localhost:4001/users/testuser",
"cc" => ["http://localhost:4001/users/testuser/followers"],
"object" => %{
"actor" => "http://localhost:4001/users/testuser",
"attachment" => [],
"cc" => ["http://localhost:4001/users/testuser/followers"],
"source" => "this is a test post",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Note"
},
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Create"
}
assert NoEmptyPolicy.filter(message) == {:ok, message}
end
test "Polls are exempt" do
message = %{
"actor" => "http://localhost:4001/users/testuser",
"cc" => ["http://localhost:4001/users/testuser/followers"],
"object" => %{
"actor" => "http://localhost:4001/users/testuser",
"attachment" => [],
"cc" => ["http://localhost:4001/users/testuser/followers"],
"oneOf" => [
%{
"name" => "chocolate",
"replies" => %{"totalItems" => 0, "type" => "Collection"},
"type" => "Note"
},
%{
"name" => "vanilla",
"replies" => %{"totalItems" => 0, "type" => "Collection"},
"type" => "Note"
}
],
"source" => "@user2",
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
"http://localhost:4001/users/user2"
],
"type" => "Question"
},
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
"http://localhost:4001/users/user2"
],
"type" => "Create"
}
assert NoEmptyPolicy.filter(message) == {:ok, message}
end
test "Notes with attachments are exempt" do
message = %{
"actor" => "http://localhost:4001/users/testuser",
"cc" => ["http://localhost:4001/users/testuser/followers"],
"object" => %{
"actor" => "http://localhost:4001/users/testuser",
"attachment" => [
%{
"actor" => "http://localhost:4001/users/testuser",
"mediaType" => "image/png",
"name" => "",
"type" => "Document",
"url" => [
%{
"href" =>
"http://localhost:4001/media/68ba231cf12e1382ce458f1979969f8ed5cc07ba198a02e653464abaf39bdb90.png",
"mediaType" => "image/png",
"type" => "Link"
}
]
}
],
"cc" => ["http://localhost:4001/users/testuser/followers"],
"source" => "@user2",
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
"http://localhost:4001/users/user2"
],
"type" => "Note"
},
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
"http://localhost:4001/users/user2"
],
"type" => "Create"
}
assert NoEmptyPolicy.filter(message) == {:ok, message}
end
test "Notes with only mentions are denied" do
message = %{
"actor" => "http://localhost:4001/users/testuser",
"cc" => ["http://localhost:4001/users/testuser/followers"],
"object" => %{
"actor" => "http://localhost:4001/users/testuser",
"attachment" => [],
"cc" => ["http://localhost:4001/users/testuser/followers"],
"source" => "@user2",
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
"http://localhost:4001/users/user2"
],
"type" => "Note"
},
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
"http://localhost:4001/users/user2"
],
"type" => "Create"
}
assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"}
end
test "Notes with no content are denied" do
message = %{
"actor" => "http://localhost:4001/users/testuser",
"cc" => ["http://localhost:4001/users/testuser/followers"],
"object" => %{
"actor" => "http://localhost:4001/users/testuser",
"attachment" => [],
"cc" => ["http://localhost:4001/users/testuser/followers"],
"source" => "",
"to" => [
"https://www.w3.org/ns/activitystreams#Public"
],
"type" => "Note"
},
"to" => [
"https://www.w3.org/ns/activitystreams#Public"
],
"type" => "Create"
}
assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"}
end
end