Merge branch 'develop' into 'new-user-emails'
authorfeld <feld@feld.me>
Mon, 8 Feb 2021 22:01:32 +0000 (22:01 +0000)
committerMark Felder <feld@feld.me>
Mon, 8 Feb 2021 22:01:50 +0000 (16:01 -0600)
15 files changed:
CHANGELOG.md
config/description.exs
docs/administration/CLI_tasks/database.md
docs/configuration/howto_search_cjk.md [new file with mode: 0644]
installation/pleroma.vcl
lib/mix/tasks/pleroma/database.ex
lib/pleroma/activity/search.ex
lib/pleroma/notification.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex [new file with mode: 0644]
lib/pleroma/web/templates/feed/feed/_activity.rss.eex
priv/repo/migrations/20210121080964_add_default_text_search_config.exs [new file with mode: 0644]
priv/repo/optional_migrations/rum_indexing/20190510135645_add_fts_index_to_objects_two.exs
test/pleroma/notification_test.exs
test/pleroma/web/activity_pub/mrf/no_empty_policy_test.exs [new file with mode: 0644]

index c1e490c304fad52296c9a562fbd5562430dcc5ac..8635ed4e7990f4db09728996247160554d8baa8d 100644 (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
 - 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
+- Admins no longer receive notifications for reports if they are the actor making the report.
+- Improved Mailer configuration setting descriptions for AdminFE.
 
 <details>
   <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.
 - Configurable background job limits for RichMedia (link previews) and MediaProxyWarmingPolicy
 - 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)
 
 <details>
index f84b52a4f6904822f75f037990a2aeacf38b09c7..d9b15e68418eeed6e515e2a6fb871b9ef49d1759 100644 (file)
@@ -99,7 +99,8 @@ config :pleroma, :config_description, [
         key: :base_url,
         label: "Base URL",
         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: [
           "https://cdn-host.com"
         ]
@@ -214,253 +215,216 @@ config :pleroma, :config_description, [
     type: :group,
     description: "Mailer-related settings",
     children: [
+      %{
+        key: :enabled,
+        label: "Mailer Enabled",
+        type: :boolean
+      },
       %{
         key: :adapter,
         type: :module,
         description:
-          "One of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters)," <>
-            " or Swoosh.Adapters.Local for in-memory mailbox",
+          "One of the mail adapters listed in [Swoosh documentation](https://hexdocs.pm/swoosh/Swoosh.html#module-adapters)",
         suggestions: [
-          Swoosh.Adapters.SMTP,
-          Swoosh.Adapters.Sendgrid,
-          Swoosh.Adapters.Sendmail,
-          Swoosh.Adapters.Mandrill,
+          Swoosh.Adapters.AmazonSES,
+          Swoosh.Adapters.Dyn,
+          Swoosh.Adapters.Gmail,
           Swoosh.Adapters.Mailgun,
           Swoosh.Adapters.Mailjet,
+          Swoosh.Adapters.Mandrill,
           Swoosh.Adapters.Postmark,
-          Swoosh.Adapters.SparkPost,
-          Swoosh.Adapters.AmazonSES,
-          Swoosh.Adapters.Dyn,
+          Swoosh.Adapters.SMTP,
+          Swoosh.Adapters.Sendgrid,
+          Swoosh.Adapters.Sendmail,
           Swoosh.Adapters.SocketLabs,
-          Swoosh.Adapters.Gmail,
-          Swoosh.Adapters.Local
+          Swoosh.Adapters.SparkPost
         ]
       },
-      %{
-        key: :enabled,
-        type: :boolean,
-        description: "Allow/disallow send emails"
-      },
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :relay,
         type: :string,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting",
-        suggestions: ["smtp.gmail.com"]
+        description: "Hostname or IP address",
+        suggestions: ["smtp.example.com"]
+      },
+      %{
+        group: {:subgroup, Swoosh.Adapters.SMTP},
+        key: :port,
+        type: :integer,
+        description: "SMTP port",
+        suggestions: ["1025"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :username,
         type: :string,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting",
-        suggestions: ["pleroma"]
+        description: "SMTP AUTH username",
+        suggestions: ["user@example.com"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :password,
         type: :string,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting",
+        description: "SMTP AUTH password",
         suggestions: ["password"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :ssl,
-        label: "SSL",
+        label: "Use SSL",
         type: :boolean,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting"
+        description: "Use Implicit SSL/TLS. e.g. port 465"
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :tls,
-        label: "TLS",
-        type: :atom,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting",
-        suggestions: [:always, :never, :if_available]
+        label: "STARTTLS Mode",
+        type: {:dropdown, :atom},
+        description: "Explicit TLS (STARTTLS) enforcement mode",
+        suggestions: [:if_available, :always, :never]
       },
       %{
         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},
-        key: :port,
-        type: :integer,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting",
-        suggestions: [1025]
+        label: "AUTH Mode",
+        type: {:dropdown, :atom},
+        description: "SMTP AUTH enforcement mode",
+        suggestions: [:if_available, :always, :never]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :retries,
         type: :integer,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting",
-        suggestions: [5]
-      },
-      %{
-        group: {:subgroup, Swoosh.Adapters.SMTP},
-        key: :no_mx_lookups,
-        label: "No MX lookups",
-        type: :boolean,
-        description: "`Swoosh.Adapters.SMTP` adapter specific setting"
+        description: "SMTP temporary (4xx) error retries",
+        suggestions: [1]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Sendgrid},
         key: :api_key,
-        label: "API key",
+        label: "SendGrid API Key",
         type: :string,
-        description: "`Swoosh.Adapters.Sendgrid` adapter specific setting",
-        suggestions: ["my-api-key"]
+        suggestions: ["YOUR_API_KEY"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Sendmail},
         key: :cmd_path,
         type: :string,
-        description: "`Swoosh.Adapters.Sendmail` adapter specific setting",
         suggestions: ["/usr/bin/sendmail"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Sendmail},
         key: :cmd_args,
         type: :string,
-        description: "`Swoosh.Adapters.Sendmail` adapter specific setting",
         suggestions: ["-N delay,failure,success"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Sendmail},
         key: :qmail,
-        type: :boolean,
-        description: "`Swoosh.Adapters.Sendmail` adapter specific setting"
+        label: "Qmail compat mode",
+        type: :boolean
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Mandrill},
         key: :api_key,
-        label: "API key",
+        label: "Mandrill API Key",
         type: :string,
-        description: "`Swoosh.Adapters.Mandrill` adapter specific setting",
-        suggestions: ["my-api-key"]
+        suggestions: ["YOUR_API_KEY"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Mailgun},
         key: :api_key,
-        label: "API key",
+        label: "Mailgun API Key",
         type: :string,
-        description: "`Swoosh.Adapters.Mailgun` adapter specific setting",
-        suggestions: ["my-api-key"]
+        suggestions: ["YOUR_API_KEY"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Mailgun},
         key: :domain,
         type: :string,
-        description: "`Swoosh.Adapters.Mailgun` adapter specific setting",
-        suggestions: ["pleroma.com"]
+        suggestions: ["YOUR_DOMAIN_NAME"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Mailjet},
         key: :api_key,
-        label: "API key",
+        label: "MailJet Public API Key",
         type: :string,
-        description: "`Swoosh.Adapters.Mailjet` adapter specific setting",
-        suggestions: ["my-api-key"]
+        suggestions: ["MJ_APIKEY_PUBLIC"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Mailjet},
         key: :secret,
+        label: "MailJet Private API Key",
         type: :string,
-        description: "`Swoosh.Adapters.Mailjet` adapter specific setting",
-        suggestions: ["my-secret-key"]
+        suggestions: ["MJ_APIKEY_PRIVATE"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Postmark},
         key: :api_key,
-        label: "API key",
+        label: "Postmark API Key",
         type: :string,
-        description: "`Swoosh.Adapters.Postmark` adapter specific setting",
-        suggestions: ["my-api-key"]
+        suggestions: ["X-Postmark-Server-Token"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SparkPost},
         key: :api_key,
-        label: "API key",
+        label: "SparkPost API key",
         type: :string,
-        description: "`Swoosh.Adapters.SparkPost` adapter specific setting",
-        suggestions: ["my-api-key"]
+        suggestions: ["YOUR_API_KEY"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SparkPost},
         key: :endpoint,
         type: :string,
-        description: "`Swoosh.Adapters.SparkPost` adapter specific setting",
         suggestions: ["https://api.sparkpost.com/api/v1"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.AmazonSES},
-        key: :region,
+        key: :access_key,
+        label: "AWS Access Key",
         type: :string,
-        description: "`Swoosh.Adapters.AmazonSES` adapter specific setting",
-        suggestions: ["us-east-1", "us-east-2"]
+        suggestions: ["AWS_ACCESS_KEY"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.AmazonSES},
-        key: :access_key,
+        key: :secret,
+        label: "AWS Secret Key",
         type: :string,
-        description: "`Swoosh.Adapters.AmazonSES` adapter specific setting",
-        suggestions: ["aws-access-key"]
+        suggestions: ["AWS_SECRET_KEY"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.AmazonSES},
-        key: :secret,
+        key: :region,
+        label: "AWS Region",
         type: :string,
-        description: "`Swoosh.Adapters.AmazonSES` adapter specific setting",
-        suggestions: ["aws-secret-key"]
+        suggestions: ["us-east-1", "us-east-2"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Dyn},
         key: :api_key,
-        label: "API key",
+        label: "Dyn API Key",
         type: :string,
-        description: "`Swoosh.Adapters.Dyn` adapter specific setting",
-        suggestions: ["my-api-key"]
+        suggestions: ["apikey"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SocketLabs},
-        key: :server_id,
+        key: :api_key,
+        label: "SocketLabs API Key",
         type: :string,
-        description: "`Swoosh.Adapters.SocketLabs` adapter specific setting"
+        suggestions: ["INJECTION_API_KEY"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SocketLabs},
-        key: :api_key,
-        label: "API key",
+        key: :server_id,
+        label: "Server ID",
         type: :string,
-        description: "`Swoosh.Adapters.SocketLabs` adapter specific setting"
+        suggestions: ["SERVER_ID"]
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Gmail},
         key: :access_token,
+        label: "GMail API Access Token",
         type: :string,
-        description: "`Swoosh.Adapters.Gmail` adapter specific setting"
-      }
-    ]
-  },
-  %{
-    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]
+        suggestions: ["GMAIL_API_ACCESS_TOKEN"]
       }
     ]
   },
@@ -1545,7 +1509,8 @@ config :pleroma, :config_description, [
           %{
             key: :max_body_length,
             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,
@@ -1595,7 +1560,7 @@ config :pleroma, :config_description, [
         key: :min_content_length,
         type: :integer,
         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 @@ config :pleroma, :config_description, [
       },
       %{
         key: :url_format,
+        label: "URL Format",
         type: :string,
         description:
           "Optional URL format preprocessing. Only required for Apache's htcacheclean.",
@@ -2888,7 +2854,7 @@ config :pleroma, :config_description, [
         type: :integer,
         description:
           "Activity pub routes (except question activities). Default: `nil` (no expiration).",
-        suggestions: [30_000, nil]
+        suggestions: [nil]
       },
       %{
         key: :activity_pub_question,
@@ -3326,9 +3292,9 @@ config :pleroma, :config_description, [
       },
       %{
         key: :ip_whitelist,
+        label: "IP Whitelist",
         type: [{:list, :string}, {:list, :charlist}, {:list, :tuple}],
-        description:
-          "[Pleroma extension] If non-empty, restricts access to app metrics endpoint to specified IP addresses."
+        description: "Restrict access of app metrics endpoint to the specified IP addresses."
       },
       %{
         key: :auth,
index 6dca8316729079c80ca997fe24f9b84b479c4795..c53c4992118796d5dd696ae4c805303e5c4be769 100644 (file)
@@ -141,3 +141,21 @@ but should only be run if necessary. **It is safe to cancel this.**
     ```sh
     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.
diff --git a/docs/configuration/howto_search_cjk.md b/docs/configuration/howto_search_cjk.md
new file mode 100644 (file)
index 0000000..d3ce280
--- /dev/null
@@ -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. 
index 13dad784c9cc219f48901845e20f83f9278a35d6..4752510ea010be1c91abb14bbf947d433d046131 100644 (file)
@@ -59,6 +59,13 @@ sub vcl_backend_response {
       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
     if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") {
       set beresp.uncacheable = true;
index 6261910f026ec29dbddd4a53d4caff09ae78147b..2403ed5813f40c6a311669f6b82fafe8f7aee184 100644 (file)
@@ -167,4 +167,51 @@ defmodule Mix.Tasks.Pleroma.Database do
     end)
     |> Stream.run()
   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
index 52e7c048d80ee27f4c4a78571bcc96e0f63b2eb4..ed898ba4ff847a79c93c15f6d9b6c7355057d50b 100644 (file)
@@ -64,7 +64,7 @@ defmodule Pleroma.Activity.Search do
     from([a, o] in q,
       where:
         fragment(
-          "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
+          "to_tsvector(?->>'content') @@ plainto_tsquery(?)",
           o.data,
           ^search_query
         )
@@ -75,7 +75,7 @@ defmodule Pleroma.Activity.Search do
     from([a, o] in q,
       where:
         fragment(
-          "to_tsvector('english', ?->>'content') @@ websearch_to_tsquery('english', ?)",
+          "to_tsvector(?->>'content') @@ websearch_to_tsquery(?)",
           o.data,
           ^search_query
         )
@@ -86,7 +86,7 @@ defmodule Pleroma.Activity.Search do
     from([a, o] in q,
       where:
         fragment(
-          "? @@ plainto_tsquery('english', ?)",
+          "? @@ plainto_tsquery(?)",
           o.fts_content,
           ^search_query
         ),
@@ -98,7 +98,7 @@ defmodule Pleroma.Activity.Search do
     from([a, o] in q,
       where:
         fragment(
-          "? @@ websearch_to_tsquery('english', ?)",
+          "? @@ websearch_to_tsquery(?)",
           o.fts_content,
           ^search_query
         ),
index 55b51321287426fd00abb22e0e8185b59066b6ee..1970fbf65d1611ac907fdeaa78aa6f660404e5d2 100644 (file)
@@ -507,8 +507,8 @@ defmodule Pleroma.Notification do
     [object_id]
   end
 
-  def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag"}}) do
-    User.all_superusers() |> Enum.map(fn user -> user.ap_id end)
+  def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do
+    (User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor]
   end
 
   def get_potential_receiver_ap_ids(activity) do
index 1a84375fb38166c5d1b9f5e2002736e330c3d2c1..5b45e2ca1dca30c5488b53c4477d73958c25aeb3 100644 (file)
@@ -377,6 +377,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          :ok <-
            maybe_federate(stripped_activity) do
       User.all_superusers()
+      |> Enum.filter(fn user -> user.ap_id != actor end)
       |> Enum.filter(fn user -> not is_nil(user.email) end)
       |> Enum.each(fn superuser ->
         superuser
diff --git a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex
new file mode 100644 (file)
index 0000000..32bb1b6
--- /dev/null
@@ -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
index 42960de7d45f58926546a23145a8d1db60ad108f..947bbb09970fa6f01788cf813414a1554eaf44a4 100644 (file)
@@ -9,7 +9,6 @@
   <ostatus:conversation ref="<%= activity_context(@activity) %>">
     <%= activity_context(@activity) %>
   </ostatus:conversation>
-  <link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
 
   <%= if @data["summary"] do %>
     <description><%= escape(@data["summary"]) %></description>
@@ -21,6 +20,8 @@
     <link><%= @data["external_url"] %></link>
   <% end %>
 
+  <link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
+
   <%= for tag <- @data["tag"] || [] do %>
     <category term="<%= tag %>"></category>
   <% end %>
diff --git a/priv/repo/migrations/20210121080964_add_default_text_search_config.exs b/priv/repo/migrations/20210121080964_add_default_text_search_config.exs
new file mode 100644 (file)
index 0000000..09b6ccc
--- /dev/null
@@ -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
index 82e02281dcbb8e80e5040dc8ba53b7b31244bd03..88476fb575e154de885b2281150ed543ffa66eb8 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Repo.Migrations.AddFtsIndexToObjectsTwo do
 
     execute("CREATE FUNCTION objects_fts_update() RETURNS trigger AS $$
     begin
-    new.fts_content := to_tsvector('english', new.data->>'content');
+    new.fts_content := to_tsvector(new.data->>'content');
     return new;
     end
     $$ LANGUAGE plpgsql")
index 0c6ebfb761d512cd352be9794c952f669bdce7ac..948587292af35746b5978bf4b30b30224e481607 100644 (file)
@@ -45,6 +45,20 @@ defmodule Pleroma.NotificationTest do
       assert notification.type == "pleroma:report"
     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
       user = insert(:user)
       other_user = insert(:user)
diff --git a/test/pleroma/web/activity_pub/mrf/no_empty_policy_test.exs b/test/pleroma/web/activity_pub/mrf/no_empty_policy_test.exs
new file mode 100644 (file)
index 0000000..fbcf684
--- /dev/null
@@ -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