Merge branch 'builder-note' into 'develop'
authorHaelwenn <contact+git.pleroma.social@hacktivis.me>
Sat, 14 Aug 2021 18:32:40 +0000 (18:32 +0000)
committerHaelwenn <contact+git.pleroma.social@hacktivis.me>
Sat, 14 Aug 2021 18:32:40 +0000 (18:32 +0000)
CommonAPI.Utils.make_note_data/1 --> ActivityPub.Builder.note/1

See merge request pleroma/pleroma!3511

62 files changed:
CHANGELOG.md
config/config.exs
config/description.exs
config/dev.exs
docs/configuration/cheatsheet.md
docs/configuration/mrf.md
docs/installation/alpine_linux_en.md
docs/installation/arch_linux_en.md
docs/installation/debian_based_en.md
docs/installation/gentoo_en.md
docs/installation/migrating_from_source_otp_en.md
docs/installation/otp_en.md
docs/installation/otp_vs_from_source.include [new file with mode: 0644]
docs/installation/otp_vs_from_source_source.include [new file with mode: 0644]
lib/pleroma/activity/search.ex
lib/pleroma/config/deprecation_warnings.ex
lib/pleroma/notification.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/mrf.ex
lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
lib/pleroma/web/activity_pub/mrf/object_age_policy.ex
lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
lib/pleroma/web/activity_pub/mrf/simple_policy.ex
lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex
lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
lib/pleroma/web/activity_pub/publisher.ex
lib/pleroma/web/activity_pub/side_effects.ex
lib/pleroma/web/admin_api/report.ex
lib/pleroma/web/api_spec/operations/notification_operation.ex
lib/pleroma/web/api_spec/operations/twitter_util_operation.ex
lib/pleroma/web/common_api/utils.ex
lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
lib/pleroma/web/mastodon_api/views/instance_view.ex
lib/pleroma/web/mastodon_api/views/notification_view.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/plugs/user_is_staff_plug.ex [new file with mode: 0644]
lib/pleroma/web/push/subscription.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/controllers/util_controller.ex
lib/pleroma/workers/poll_worker.ex [new file with mode: 0644]
mix.exs
priv/gettext/pl/LC_MESSAGES/errors.po
priv/repo/migrations/20201005123100_simple_policy_string_to_tuple.exs [new file with mode: 0644]
priv/repo/migrations/20201005124600_quarantained_policy_string_to_tuple.exs [new file with mode: 0644]
priv/repo/migrations/20201005132900_transparency_exclusions_string_to_tuple.exs [new file with mode: 0644]
priv/repo/migrations/20210717000000_add_poll_to_notifications_enum.exs [new file with mode: 0644]
test/pleroma/config/deprecation_warnings_test.exs
test/pleroma/notification_test.exs
test/pleroma/user_test.exs
test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs
test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
test/pleroma/web/activity_pub/mrf_test.exs
test/pleroma/web/activity_pub/publisher_test.exs
test/pleroma/web/activity_pub/side_effects_test.exs
test/pleroma/web/admin_api/controllers/report_controller_test.exs
test/pleroma/web/common_api_test.exs
test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
test/pleroma/web/mastodon_api/views/notification_view_test.exs
test/pleroma/web/node_info_test.exs
test/pleroma/web/plugs/user_is_staff_plug_test.exs [new file with mode: 0644]
test/pleroma/web/twitter_api/util_controller_test.exs
test/support/factory.ex

index 4e3e408646f1aad4403ca427ee2d67914bf62472..231cac990c0c82f38313f0c5bb56aee656472884 100644 (file)
@@ -11,14 +11,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 ### Added
 
 ### Fixed
+- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
 
 ### Removed
 
+## Unreleased-patch
+- Mastodon API: Activity Search fallbacks on status fetching after a DB Timeout/Error
+
 ## 2.4.0 - 2021-08-xx
 
 ### Changed
 
 - **Breaking:** Configuration: `:chat, enabled` moved to `:shout, enabled` and `:instance, chat_limit` moved to `:shout, limit`
+- **Breaking** Entries for simple_policy, transparency_exclusions and quarantined_instances now list both the instance and a reason.
 - Support for Erlang/OTP 24
 - The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
 - HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
@@ -34,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - AdminAPI: return `created_at` date with users.
 - `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time.
 - Attachment dimensions and blurhashes are federated when available.
+- Mastodon API: support `poll` notification.
 - Pinned posts federation
 
 ### Fixed
index b50c910b12b985ed1c8f6311fc6b8a25c6f8c550..828fe00857518594c69495ae44f705c9debd8b71 100644 (file)
@@ -560,6 +560,7 @@ config :pleroma, Oban,
     mailer: 10,
     transmogrifier: 20,
     scheduled_activities: 10,
+    poll_notifications: 10,
     background: 5,
     remote_fetcher: 2,
     attachments_cleanup: 1,
index 934a62a6293d77479affb6649320045d13ab8e24..c72231faa5ef479b117e413290d96fc9d16ef025 100644 (file)
@@ -687,12 +687,14 @@ config :pleroma, :config_description, [
       },
       %{
         key: :quarantined_instances,
-        type: {:list, :string},
+        type: {:list, :tuple},
+        key_placeholder: "instance",
+        value_placeholder: "reason",
         description:
-          "List of ActivityPub instances where private (DMs, followers-only) activities will not be sent",
+          "List of ActivityPub instances where private (DMs, followers-only) activities will not be sent and the reason for doing so",
         suggestions: [
-          "quarantined.com",
-          "*.quarantined.com"
+          {"quarantined.com", "Reason"},
+          {"*.quarantined.com", "Reason"}
         ]
       },
       %{
index 6b7ffb0e9064f7c3a19b19622c69398239219955..ab3e83c12027a96efdbbc22430094dac3a0b905e 100644 (file)
@@ -62,6 +62,7 @@ if File.exists?("./config/dev.secret.exs") do
   import_config "dev.secret.exs"
 else
   IO.puts(
+    :stderr,
     "!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs"
   )
 end
index 5b49185dc2c056dfc3cc4353f03f8fef893b079c..d3c9c5716e7928d69fc905f897f65590a30b29b3 100644 (file)
@@ -39,7 +39,7 @@ To add configuration to your config file, you can copy it from the base config.
 * `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
 * `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
 * `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
-* `quarantined_instances`: List of ActivityPub instances where private (DMs, followers-only) activities will not be send.
+* `quarantined_instances`: ActivityPub instances where private (DMs, followers-only) activities will not be send.
 * `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
 * `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
     older software for theses nicknames.
@@ -135,15 +135,16 @@ To add configuration to your config file, you can copy it from the base config.
     Configuring MRF policies is not enough for them to take effect. You have to enable them by specifying their module in `policies` under [:mrf](#mrf) section.
 
 #### :mrf_simple
-* `media_removal`: List of instances to remove media from.
-* `media_nsfw`: List of instances to put media as NSFW(sensitive) from.
-* `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline.
-* `reject`: List of instances to reject any activities from.
-* `accept`: List of instances to accept any activities from.
-* `followers_only`: List of instances to decrease post visibility to only the followers, including for DM mentions.
-* `report_removal`: List of instances to reject reports from.
-* `avatar_removal`: List of instances to strip avatars from.
-* `banner_removal`: List of instances to strip banners from.
+* `media_removal`: List of instances to strip media attachments from and the reason for doing so.
+* `media_nsfw`: List of instances to tag all media as NSFW (sensitive) from and the reason for doing so.
+* `federated_timeline_removal`: List of instances to remove from the Federated Timeline (aka The Whole Known Network) and the reason for doing so.
+* `reject`: List of instances to reject activities (except deletes) from and the reason for doing so.
+* `accept`: List of instances to only accept activities (except deletes) from and the reason for doing so.
+* `followers_only`: Force posts from the given instances to be visible by followers only and the reason for doing so.
+* `report_removal`: List of instances to reject reports from and the reason for doing so.
+* `avatar_removal`: List of instances to strip avatars from and the reason for doing so.
+* `banner_removal`: List of instances to strip banners from and the reason for doing so.
+* `reject_deletes`: List of instances to reject deletions from and the reason for doing so.
 
 #### :mrf_subchain
 This policy processes messages through an alternate pipeline when a given message matches certain criteria.
index 5618634a20d4a2ffedb7866de0c5be8981038b91..a31c26b9c1812b2aec33c9a810cbb00cef4e1358 100644 (file)
@@ -55,18 +55,18 @@ Servers should be configured as lists.
 
 ### Example
 
-This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
+This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`. We also give a reason why the moderation was done:
 
 ```elixir
 config :pleroma, :mrf,
   policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
 
 config :pleroma, :mrf_simple,
-  media_removal: ["illegalporn.biz"],
-  media_nsfw: ["porn.biz", "porn.business"],
-  reject: ["spam.com"],
-  federated_timeline_removal: ["spam.university"],
-  report_removal: ["whiny.whiner"]
+  media_removal: [{"illegalporn.biz", "Media can contain illegal contant"}],
+  media_nsfw: [{"porn.biz", "unmarked nsfw media"}, {"porn.business", "A lot of unmarked nsfw media"}],
+  reject: [{"spam.com", "They keep spamming our users"}],
+  federated_timeline_removal: [{"spam.university", "Annoying low-quality posts who otherwise fill up TWKN"}],
+  report_removal: [{"whiny.whiner", "Keep spamming us with irrelevant reports"}]
 ```
 
 ### Use with Care
index 13395ff25ebb4c3c86e4b33143c873f66c0ca978..c37ff0c636c2c40002cca5d76a6fc776d4fa4b83 100644 (file)
@@ -1,4 +1,7 @@
 # Installing on Alpine Linux
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
 ## Installation
 
 This guide is a step-by-step installation guide for Alpine Linux. The instructions were verified against Alpine v3.10 standard image. You might miss additional dependencies if you use `netboot` instead.
index d11deb6210cc1e0c70c74db4fcbe61d41c5a150f..285743d564197cc00bbf1af7ef0762f1971603e8 100644 (file)
@@ -1,4 +1,7 @@
 # Installing on Arch Linux
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
 ## Installation
 
 This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.archlinux.org/index.php/Sudo). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
index 02682e5b0c4aebea784a54712fc7ca699aef0455..4e52b2155b6de0c3055d2d13b08ed1055fd4e211 100644 (file)
@@ -1,4 +1,7 @@
 # Installing on Debian Based Distributions
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
 ## Installation
 
 This guide will assume you are on Debian 11 (“bullseye”) or later. This guide should also work with Ubuntu 18.04 (“Bionic Beaver”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
index 982ab52d2b9ef5d530206edda4dcd772d6ec3bd8..36882c8c86bf88d7ced69882fcba70cb95a2983c 100644 (file)
@@ -1,4 +1,7 @@
 # Installing on Gentoo GNU/Linux
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
 ## Installation
 
 This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.gentoo.org/wiki/Sudo). Lines that begin with `#` indicate that they should be run as the superuser. Lines using `$` should be run as the indicated user, e.g. `pleroma$` should be run as the `pleroma` user.
index d303a6daf4807b681eb2334030b65be6a2786cac..e4a01d8db37c02cbcfebdb9aa60069bc852cfa40 100644 (file)
@@ -1,7 +1,8 @@
 # Switching a from-source install to OTP releases
 
-## What are OTP releases?
-OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more.
+{! backend/installation/otp_vs_from_source.include !}
+
+In this guide we cover how you can migrate from a from source installation to one using OTP releases.
 
 ## Pre-requisites
 You will be running commands as root. If you aren't root already, please elevate your priviledges by executing `sudo su`/`su`.
index 3f67534ac65c4b1a6b2a5ee4a79e1e843634ab86..0861a8157fb59c3938da1627498ae3033be40476 100644 (file)
@@ -1,5 +1,9 @@
 # Installing on Linux using OTP releases
 
+{! backend/installation/otp_vs_from_source.include !}
+
+This guide covers a installation using an OTP release. To install Pleroma from source, please check out the corresponding guide for your distro.
+
 ## Pre-requisites
 * A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
 * A (sub)domain pointed to the machine
diff --git a/docs/installation/otp_vs_from_source.include b/docs/installation/otp_vs_from_source.include
new file mode 100644 (file)
index 0000000..63e837a
--- /dev/null
@@ -0,0 +1,3 @@
+## OTP releases vs from-source installations
+
+There are two ways to install Pleroma. You can use OTP releases or do a from-source installation. OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more. With from source installations you install Pleroma from source, meaning you have to install certain dependencies like Erlang+Elixir and compile Pleroma yourself.
diff --git a/docs/installation/otp_vs_from_source_source.include b/docs/installation/otp_vs_from_source_source.include
new file mode 100644 (file)
index 0000000..63482b6
--- /dev/null
@@ -0,0 +1,3 @@
+{! backend/installation/otp_vs_from_source.include !}
+
+This guide covers a from-source installation. To install using OTP releases, please check out [the OTP guide](./otp_en.md).
index ed898ba4ff847a79c93c15f6d9b6c7355057d50b..a5923519c3d7434534d848534093cd38144c99f1 100644 (file)
@@ -26,19 +26,23 @@ defmodule Pleroma.Activity.Search do
         :plain
       end
 
-    Activity
-    |> Activity.with_preloaded_object()
-    |> Activity.restrict_deactivated_users()
-    |> restrict_public()
-    |> query_with(index_type, search_query, search_function)
-    |> maybe_restrict_local(user)
-    |> maybe_restrict_author(author)
-    |> maybe_restrict_blocked(user)
-    |> Pagination.fetch_paginated(
-      %{"offset" => offset, "limit" => limit, "skip_order" => index_type == :rum},
-      :offset
-    )
-    |> maybe_fetch(user, search_query)
+    try do
+      Activity
+      |> Activity.with_preloaded_object()
+      |> Activity.restrict_deactivated_users()
+      |> restrict_public()
+      |> query_with(index_type, search_query, search_function)
+      |> maybe_restrict_local(user)
+      |> maybe_restrict_author(author)
+      |> maybe_restrict_blocked(user)
+      |> Pagination.fetch_paginated(
+        %{"offset" => offset, "limit" => limit, "skip_order" => index_type == :rum},
+        :offset
+      )
+      |> maybe_fetch(user, search_query)
+    rescue
+      _ -> maybe_fetch([], user, search_query)
+    end
   end
 
   def maybe_restrict_author(query, %User{} = author) do
index fedd58a7ef584bc09e9da5467d4583b80edde24e..029ee8b6526c5166fa6d2e9a8b49a6e65403a569 100644 (file)
@@ -20,6 +20,140 @@ defmodule Pleroma.Config.DeprecationWarnings do
      "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
   ]
 
+  def check_simple_policy_tuples do
+    has_strings =
+      Config.get([:mrf_simple])
+      |> Enum.any?(fn {_, v} -> Enum.any?(v, &is_binary/1) end)
+
+    if has_strings do
+      Logger.warn("""
+      !!!DEPRECATION WARNING!!!
+      Your config is using strings in the SimplePolicy configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+      ```
+      config :pleroma, :mrf_simple,
+        media_removal: ["instance.tld"],
+        media_nsfw: ["instance.tld"],
+        federated_timeline_removal: ["instance.tld"],
+        report_removal: ["instance.tld"],
+        reject: ["instance.tld"],
+        followers_only: ["instance.tld"],
+        accept: ["instance.tld"],
+        avatar_removal: ["instance.tld"],
+        banner_removal: ["instance.tld"],
+        reject_deletes: ["instance.tld"]
+      ```
+
+      Is now
+
+
+      ```
+      config :pleroma, :mrf_simple,
+        media_removal: [{"instance.tld", "Reason for media removal"}],
+        media_nsfw: [{"instance.tld", "Reason for media nsfw"}],
+        federated_timeline_removal: [{"instance.tld", "Reason for federated timeline removal"}],
+        report_removal: [{"instance.tld", "Reason for report removal"}],
+        reject: [{"instance.tld", "Reason for reject"}],
+        followers_only: [{"instance.tld", "Reason for followers only"}],
+        accept: [{"instance.tld", "Reason for accept"}],
+        avatar_removal: [{"instance.tld", "Reason for avatar removal"}],
+        banner_removal: [{"instance.tld", "Reason for banner removal"}],
+        reject_deletes: [{"instance.tld", "Reason for reject deletes"}]
+      ```
+      """)
+
+      new_config =
+        Config.get([:mrf_simple])
+        |> Enum.map(fn {k, v} ->
+          {k,
+           Enum.map(v, fn
+             {instance, reason} -> {instance, reason}
+             instance -> {instance, ""}
+           end)}
+        end)
+
+      Config.put([:mrf_simple], new_config)
+
+      :error
+    else
+      :ok
+    end
+  end
+
+  def check_quarantined_instances_tuples do
+    has_strings = Config.get([:instance, :quarantined_instances]) |> Enum.any?(&is_binary/1)
+
+    if has_strings do
+      Logger.warn("""
+      !!!DEPRECATION WARNING!!!
+      Your config is using strings in the quarantined_instances configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+      ```
+      config :pleroma, :instance,
+        quarantined_instances: ["instance.tld"]
+      ```
+
+      Is now
+
+
+      ```
+      config :pleroma, :instance,
+        quarantined_instances: [{"instance.tld", "Reason for quarantine"}]
+      ```
+      """)
+
+      new_config =
+        Config.get([:instance, :quarantined_instances])
+        |> Enum.map(fn
+          {instance, reason} -> {instance, reason}
+          instance -> {instance, ""}
+        end)
+
+      Config.put([:instance, :quarantined_instances], new_config)
+
+      :error
+    else
+      :ok
+    end
+  end
+
+  def check_transparency_exclusions_tuples do
+    has_strings = Config.get([:mrf, :transparency_exclusions]) |> Enum.any?(&is_binary/1)
+
+    if has_strings do
+      Logger.warn("""
+      !!!DEPRECATION WARNING!!!
+      Your config is using strings in the transparency_exclusions configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+      ```
+      config :pleroma, :mrf,
+        transparency_exclusions: ["instance.tld"]
+      ```
+
+      Is now
+
+
+      ```
+      config :pleroma, :mrf,
+        transparency_exclusions: [{"instance.tld", "Reason to exlude transparency"}]
+      ```
+      """)
+
+      new_config =
+        Config.get([:mrf, :transparency_exclusions])
+        |> Enum.map(fn
+          {instance, reason} -> {instance, reason}
+          instance -> {instance, ""}
+        end)
+
+      Config.put([:mrf, :transparency_exclusions], new_config)
+
+      :error
+    else
+      :ok
+    end
+  end
+
   def check_hellthread_threshold do
     if Config.get([:mrf_hellthread, :threshold]) do
       Logger.warn("""
@@ -34,20 +168,24 @@ defmodule Pleroma.Config.DeprecationWarnings do
   end
 
   def warn do
-    with :ok <- check_hellthread_threshold(),
-         :ok <- check_old_mrf_config(),
-         :ok <- check_media_proxy_whitelist_config(),
-         :ok <- check_welcome_message_config(),
-         :ok <- check_gun_pool_options(),
-         :ok <- check_activity_expiration_config(),
-         :ok <- check_remote_ip_plug_name(),
-         :ok <- check_uploders_s3_public_endpoint(),
-         :ok <- check_old_chat_shoutbox() do
-      :ok
-    else
-      _ ->
-        :error
-    end
+    [
+      check_hellthread_threshold(),
+      check_old_mrf_config(),
+      check_media_proxy_whitelist_config(),
+      check_welcome_message_config(),
+      check_gun_pool_options(),
+      check_activity_expiration_config(),
+      check_remote_ip_plug_name(),
+      check_uploders_s3_public_endpoint(),
+      check_old_chat_shoutbox(),
+      check_quarantined_instances_tuples(),
+      check_transparency_exclusions_tuples(),
+      check_simple_policy_tuples()
+    ]
+    |> Enum.reduce(:ok, fn
+      :ok, :ok -> :ok
+      _, _ -> :error
+    end)
   end
 
   def check_welcome_message_config do
index 7efbdc49afe1811a145a78cc548ad0dd54935849..32f13df69c31a6400d77156b6fdfcaba36e0289d 100644 (file)
@@ -72,6 +72,7 @@ defmodule Pleroma.Notification do
     pleroma:emoji_reaction
     pleroma:report
     reblog
+    poll
   }
 
   def changeset(%Notification{} = notification, attrs) do
@@ -379,7 +380,7 @@ defmodule Pleroma.Notification do
     notifications =
       Enum.map(potential_receivers, fn user ->
         do_send = do_send && user in enabled_receivers
-        create_notification(activity, user, do_send)
+        create_notification(activity, user, do_send: do_send)
       end)
       |> Enum.reject(&is_nil/1)
 
@@ -435,15 +436,18 @@ defmodule Pleroma.Notification do
   end
 
   # TODO move to sql, too.
-  def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do
-    unless skip?(activity, user) do
+  def create_notification(%Activity{} = activity, %User{} = user, opts \\ []) do
+    do_send = Keyword.get(opts, :do_send, true)
+    type = Keyword.get(opts, :type, type_from_activity(activity))
+
+    unless skip?(activity, user, opts) do
       {:ok, %{notification: notification}} =
         Multi.new()
         |> Multi.insert(:notification, %Notification{
           user_id: user.id,
           activity: activity,
           seen: mark_as_read?(activity, user),
-          type: type_from_activity(activity)
+          type: type
         })
         |> Marker.multi_set_last_read_id(user, "notifications")
         |> Repo.transaction()
@@ -457,6 +461,28 @@ defmodule Pleroma.Notification do
     end
   end
 
+  def create_poll_notifications(%Activity{} = activity) do
+    with %Object{data: %{"type" => "Question", "actor" => actor} = data} <-
+           Object.normalize(activity) do
+      voters =
+        case data do
+          %{"voters" => voters} when is_list(voters) -> voters
+          _ -> []
+        end
+
+      notifications =
+        Enum.reduce([actor | voters], [], fn ap_id, acc ->
+          with %User{local: true} = user <- User.get_by_ap_id(ap_id) do
+            [create_notification(activity, user, type: "poll") | acc]
+          else
+            _ -> acc
+          end
+        end)
+
+      {:ok, notifications}
+    end
+  end
+
   @doc """
   Returns a tuple with 2 elements:
     {notification-enabled receivers, currently disabled receivers (blocking / [thread] muting)}
@@ -572,8 +598,10 @@ defmodule Pleroma.Notification do
     Enum.uniq(ap_ids) -- thread_muter_ap_ids
   end
 
-  @spec skip?(Activity.t(), User.t()) :: boolean()
-  def skip?(%Activity{} = activity, %User{} = user) do
+  def skip?(activity, user, opts \\ [])
+
+  @spec skip?(Activity.t(), User.t(), Keyword.t()) :: boolean()
+  def skip?(%Activity{} = activity, %User{} = user, opts) do
     [
       :self,
       :invisible,
@@ -581,17 +609,21 @@ defmodule Pleroma.Notification do
       :recently_followed,
       :filtered
     ]
-    |> Enum.find(&skip?(&1, activity, user))
+    |> Enum.find(&skip?(&1, activity, user, opts))
   end
 
-  def skip?(_, _), do: false
+  def skip?(_activity, _user, _opts), do: false
 
-  @spec skip?(atom(), Activity.t(), User.t()) :: boolean()
-  def skip?(:self, %Activity{} = activity, %User{} = user) do
-    activity.data["actor"] == user.ap_id
+  @spec skip?(atom(), Activity.t(), User.t(), Keyword.t()) :: boolean()
+  def skip?(:self, %Activity{} = activity, %User{} = user, opts) do
+    cond do
+      opts[:type] == "poll" -> false
+      activity.data["actor"] == user.ap_id -> true
+      true -> false
+    end
   end
 
-  def skip?(:invisible, %Activity{} = activity, _) do
+  def skip?(:invisible, %Activity{} = activity, _user, _opts) do
     actor = activity.data["actor"]
     user = User.get_cached_by_ap_id(actor)
     User.invisible?(user)
@@ -600,15 +632,27 @@ defmodule Pleroma.Notification do
   def skip?(
         :block_from_strangers,
         %Activity{} = activity,
-        %User{notification_settings: %{block_from_strangers: true}} = user
+        %User{notification_settings: %{block_from_strangers: true}} = user,
+        opts
       ) do
     actor = activity.data["actor"]
     follower = User.get_cached_by_ap_id(actor)
-    !User.following?(follower, user)
+
+    cond do
+      opts[:type] == "poll" -> false
+      user.ap_id == actor -> false
+      !User.following?(follower, user) -> true
+      true -> false
+    end
   end
 
   # To do: consider defining recency in hours and checking FollowingRelationship with a single SQL
-  def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity, %User{} = user) do
+  def skip?(
+        :recently_followed,
+        %Activity{data: %{"type" => "Follow"}} = activity,
+        %User{} = user,
+        _opts
+      ) do
     actor = activity.data["actor"]
 
     Notification.for_user(user)
@@ -618,9 +662,10 @@ defmodule Pleroma.Notification do
     end)
   end
 
-  def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false
+  def skip?(:filtered, %{data: %{"type" => type}}, _user, _opts) when type in ["Follow", "Move"],
+    do: false
 
-  def skip?(:filtered, activity, user) do
+  def skip?(:filtered, activity, user, _opts) do
     object = Object.normalize(activity, fetch: false)
 
     cond do
@@ -638,7 +683,7 @@ defmodule Pleroma.Notification do
     end
   end
 
-  def skip?(_, _, _), do: false
+  def skip?(_type, _activity, _user, _opts), do: false
 
   def mark_as_read?(activity, target_user) do
     user = Activity.user_actor(activity)
index 4c29dda355534fcae437700e130e4128ed0c515e..19961a4a55ea5b9370922d1df63ca0b9330fe00a 100644 (file)
@@ -25,6 +25,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   alias Pleroma.Web.Streamer
   alias Pleroma.Web.WebFinger
   alias Pleroma.Workers.BackgroundWorker
+  alias Pleroma.Workers.PollWorker
 
   import Ecto.Query
   import Pleroma.Web.ActivityPub.Utils
@@ -288,6 +289,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
          {:ok, _actor} <- increase_note_count_if_public(actor, activity),
          _ <- notify_and_stream(activity),
+         :ok <- maybe_schedule_poll_notifications(activity),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
     else
@@ -302,6 +304,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
+  defp maybe_schedule_poll_notifications(activity) do
+    PollWorker.schedule_poll_end(activity)
+    :ok
+  end
+
   @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
   def listen(%{to: to, actor: actor, context: context, object: object} = params) do
     additional = params[:additional] || %{}
index ac00fa54ba27f541851edcf939ed25c54946cb2d..23ea039c34b24af5903c5a14d4338930887ab39c 100644 (file)
@@ -33,9 +33,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do
         %{
           key: :transparency_exclusions,
           label: "MRF transparency exclusions",
-          type: {:list, :string},
+          type: {:list, :tuple},
+          key_placeholder: "instance",
+          value_placeholder: "reason",
           description:
-            "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
+            "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. You can also provide a reason for excluding these instance names. The instances and reasons won't be publicly disclosed.",
           suggestions: [
             "exclusion.com"
           ]
@@ -100,6 +102,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do
     Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
   end
 
+  @spec instance_list_from_tuples([{String.t(), String.t()}]) :: [String.t()]
+  def instance_list_from_tuples(list) do
+    Enum.map(list, fn {instance, _} -> instance end)
+  end
+
   def describe(policies) do
     {:ok, policy_configs} =
       policies
index 646008dd9abb5b033e9516b48fddf8afdcf1cdd8..1383fa757365836c2d102212f2c0c9868cb9ff62 100644 (file)
@@ -159,6 +159,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
         %{
           key: :replace,
           type: {:list, :tuple},
+          key_placeholder: "instance",
+          value_placeholder: "reason",
           description: """
             **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
 
index 9a211fd447fc4d9020dd38f9e880b226c7344de7..02c9b18ed5eb5203f3496577e3ff4c9ad80c07d8 100644 (file)
@@ -49,6 +49,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
           message
           |> Map.put("to", to)
           |> Map.put("cc", cc)
+          |> Kernel.put_in(["object", "to"], to)
+          |> Kernel.put_in(["object", "cc"], cc)
 
         {:ok, message}
       else
@@ -70,6 +72,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
           message
           |> Map.put("to", to)
           |> Map.put("cc", cc)
+          |> Kernel.put_in(["object", "to"], to)
+          |> Kernel.put_in(["object", "cc"], cc)
 
         {:ok, message}
       else
@@ -82,7 +86,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
   end
 
   @impl true
-  def filter(%{"type" => "Create", "published" => _} = message) do
+  def filter(%{"type" => "Create", "object" => %{"published" => _}} = message) do
     with actions <- Config.get([:mrf_object_age, :actions]),
          {:reject, _} <- check_date(message),
          {:ok, message} <- check_reject(message, actions),
index b9d3e52c7eeb94ac8223fec06caa45da6e313941..dbb7ca0df7b08c0a41af63f21fa697cd8bbd64e0 100644 (file)
@@ -47,7 +47,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
 
   @impl true
   def describe,
-    do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
+    do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Map.new()}}
 
   @impl true
   def config_description do
index 30562ac085bd193d44cd3f78bc43f4dd581c2a08..c631cc85fad4c814107a8c6d369dfcf66cd82537 100644 (file)
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_accept(%{host: actor_host} = _actor_info, object) do
     accepts =
-      Config.get([:mrf_simple, :accept])
+      instance_list(:accept)
       |> MRF.subdomains_regex()
 
     cond do
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_reject(%{host: actor_host} = _actor_info, object) do
     rejects =
-      Config.get([:mrf_simple, :reject])
+      instance_list(:reject)
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(rejects, actor_host) do
@@ -44,7 +44,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
        )
        when length(child_attachment) > 0 do
     media_removal =
-      Config.get([:mrf_simple, :media_removal])
+      instance_list(:media_removal)
       |> MRF.subdomains_regex()
 
     object =
@@ -68,7 +68,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
          } = object
        ) do
     media_nsfw =
-      Config.get([:mrf_simple, :media_nsfw])
+      instance_list(:media_nsfw)
       |> MRF.subdomains_regex()
 
     object =
@@ -85,7 +85,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
     timeline_removal =
-      Config.get([:mrf_simple, :federated_timeline_removal])
+      instance_list(:federated_timeline_removal)
       |> MRF.subdomains_regex()
 
     object =
@@ -112,7 +112,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_followers_only(%{host: actor_host} = _actor_info, object) do
     followers_only =
-      Config.get([:mrf_simple, :followers_only])
+      instance_list(:followers_only)
       |> MRF.subdomains_regex()
 
     object =
@@ -137,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
     report_removal =
-      Config.get([:mrf_simple, :report_removal])
+      instance_list(:report_removal)
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(report_removal, actor_host) do
@@ -151,7 +151,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
     avatar_removal =
-      Config.get([:mrf_simple, :avatar_removal])
+      instance_list(:avatar_removal)
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(avatar_removal, actor_host) do
@@ -165,7 +165,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
     banner_removal =
-      Config.get([:mrf_simple, :banner_removal])
+      instance_list(:banner_removal)
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(banner_removal, actor_host) do
@@ -185,12 +185,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_object(object), do: {:ok, object}
 
+  defp instance_list(config_key) do
+    Config.get([:mrf_simple, config_key])
+    |> MRF.instance_list_from_tuples()
+  end
+
   @impl true
   def filter(%{"type" => "Delete", "actor" => actor} = object) do
     %{host: actor_host} = URI.parse(actor)
 
     reject_deletes =
-      Config.get([:mrf_simple, :reject_deletes])
+      instance_list(:reject_deletes)
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(reject_deletes, actor_host) do
@@ -253,14 +258,42 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   @impl true
   def describe do
-    exclusions = Config.get([:mrf, :transparency_exclusions])
+    exclusions = Config.get([:mrf, :transparency_exclusions]) |> MRF.instance_list_from_tuples()
 
-    mrf_simple =
+    mrf_simple_excluded =
       Config.get(:mrf_simple)
-      |> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end)
-      |> Enum.into(%{})
+      |> Enum.map(fn {rule, instances} ->
+        {rule, Enum.reject(instances, fn {host, _} -> host in exclusions end)}
+      end)
 
-    {:ok, %{mrf_simple: mrf_simple}}
+    mrf_simple =
+      mrf_simple_excluded
+      |> Enum.map(fn {rule, instances} ->
+        {rule, Enum.map(instances, fn {host, _} -> host end)}
+      end)
+      |> Map.new()
+
+    # This is for backwards compatibility. We originally didn't sent
+    # extra info like a reason why an instance was rejected/quarantined/etc.
+    # Because we didn't want to break backwards compatibility it was decided
+    # to add an extra "info" key.
+    mrf_simple_info =
+      mrf_simple_excluded
+      |> Enum.map(fn {rule, instances} ->
+        {rule, Enum.reject(instances, fn {_, reason} -> reason == "" end)}
+      end)
+      |> Enum.reject(fn {_, instances} -> instances == [] end)
+      |> Enum.map(fn {rule, instances} ->
+        instances =
+          instances
+          |> Enum.map(fn {host, reason} -> {host, %{"reason" => reason}} end)
+          |> Map.new()
+
+        {rule, instances}
+      end)
+      |> Map.new()
+
+    {:ok, %{mrf_simple: mrf_simple, mrf_simple_info: mrf_simple_info}}
   end
 
   @impl true
@@ -270,70 +303,67 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
       related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
       label: "MRF Simple",
       description: "Simple ingress policies",
-      children: [
-        %{
-          key: :media_removal,
-          type: {:list, :string},
-          description: "List of instances to strip media attachments from",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :media_nsfw,
-          label: "Media NSFW",
-          type: {:list, :string},
-          description: "List of instances to tag all media as NSFW (sensitive) from",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :federated_timeline_removal,
-          type: {:list, :string},
-          description:
-            "List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :reject,
-          type: {:list, :string},
-          description: "List of instances to reject activities from (except deletes)",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :accept,
-          type: {:list, :string},
-          description: "List of instances to only accept activities from (except deletes)",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :followers_only,
-          type: {:list, :string},
-          description: "Force posts from the given instances to be visible by followers only",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :report_removal,
-          type: {:list, :string},
-          description: "List of instances to reject reports from",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :avatar_removal,
-          type: {:list, :string},
-          description: "List of instances to strip avatars from",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :banner_removal,
-          type: {:list, :string},
-          description: "List of instances to strip banners from",
-          suggestions: ["example.com", "*.example.com"]
-        },
-        %{
-          key: :reject_deletes,
-          type: {:list, :string},
-          description: "List of instances to reject deletions from",
-          suggestions: ["example.com", "*.example.com"]
-        }
-      ]
+      children:
+        [
+          %{
+            key: :media_removal,
+            description:
+              "List of instances to strip media attachments from and the reason for doing so"
+          },
+          %{
+            key: :media_nsfw,
+            label: "Media NSFW",
+            description:
+              "List of instances to tag all media as NSFW (sensitive) from and the reason for doing so"
+          },
+          %{
+            key: :federated_timeline_removal,
+            description:
+              "List of instances to remove from the Federated (aka The Whole Known Network) Timeline and the reason for doing so"
+          },
+          %{
+            key: :reject,
+            description:
+              "List of instances to reject activities from (except deletes) and the reason for doing so"
+          },
+          %{
+            key: :accept,
+            description:
+              "List of instances to only accept activities from (except deletes) and the reason for doing so"
+          },
+          %{
+            key: :followers_only,
+            description:
+              "Force posts from the given instances to be visible by followers only and the reason for doing so"
+          },
+          %{
+            key: :report_removal,
+            description: "List of instances to reject reports from and the reason for doing so"
+          },
+          %{
+            key: :avatar_removal,
+            description: "List of instances to strip avatars from and the reason for doing so"
+          },
+          %{
+            key: :banner_removal,
+            description: "List of instances to strip banners from and the reason for doing so"
+          },
+          %{
+            key: :reject_deletes,
+            description: "List of instances to reject deletions from and the reason for doing so"
+          }
+        ]
+        |> Enum.map(fn setting ->
+          Map.merge(
+            setting,
+            %{
+              type: {:list, :tuple},
+              key_placeholder: "instance",
+              value_placeholder: "reason",
+              suggestions: [{"example.com", "Some reason"}, {"*.example.com", "Another reason"}]
+            }
+          )
+        end)
     }
   end
 end
index 1bcb3688ba5716670987d023dca9381076eb6e41..52fb02a84bebd1695bf9541a9dc998587228e4fa 100644 (file)
@@ -37,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
   def describe do
     mrf_user_allowlist =
       Config.get([:mrf_user_allowlist], [])
-      |> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)
+      |> Map.new(fn {k, v} -> {k, length(v)} end)
 
     {:ok, %{mrf_user_allowlist: mrf_user_allowlist}}
   end
index 20f57f60983b1fa7a09ca47cbaaf8b8f3b67ace0..602e10b44500b6ead903cf8aad7df0b65b94dcf7 100644 (file)
@@ -39,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
 
   @impl true
   def describe,
-    do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
+    do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Map.new()}}
 
   @impl true
   def config_description do
index 590beef64a19cdbf89fe17c45a74f3a478cf7c3d..4f29a441136a2377eb3a7f1cd6ea07430c708e86 100644 (file)
@@ -112,6 +112,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
 
       quarantined_instances =
         Config.get([:instance, :quarantined_instances], [])
+        |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples()
         |> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
 
       !Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host)
index b0ec84ade17cb44309759b0de73861c114159036..701181a1419e2f12bce3932fba3d0014832019da 100644 (file)
@@ -10,7 +10,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
   collection, and so on.
   """
   alias Pleroma.Activity
-  alias Pleroma.Activity.Ir.Topics
   alias Pleroma.Chat
   alias Pleroma.Chat.MessageReference
   alias Pleroma.FollowingRelationship
@@ -24,6 +23,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.Push
   alias Pleroma.Web.Streamer
+  alias Pleroma.Workers.PollWorker
 
   require Logger
 
@@ -195,7 +195,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
   # - Set up notifications
   @impl true
   def handle(%{data: %{"type" => "Create"}} = activity, meta) do
-    with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta),
+    with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta),
          %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
       {:ok, notifications} = Notification.create_notifications(activity, do_send: false)
       {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
@@ -225,6 +225,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
         meta
         |> add_notifications(notifications)
 
+      ap_streamer().stream_out(activity)
+
       {:ok, activity, meta}
     else
       e -> Repo.rollback(e)
@@ -245,9 +247,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
     if !User.is_internal_user?(user) do
       Notification.create_notifications(object)
 
-      object
-      |> Topics.get_activity_topics()
-      |> Streamer.stream(object)
+      ap_streamer().stream_out(object)
     end
 
     {:ok, object, meta}
@@ -389,7 +389,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
     {:ok, object, meta}
   end
 
-  def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do
+  def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do
     with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
       actor = User.get_cached_by_ap_id(object.data["actor"])
       recipient = User.get_cached_by_ap_id(hd(object.data["to"]))
@@ -424,7 +424,14 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
     end
   end
 
-  def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
+  def handle_object_creation(%{"type" => "Question"} = object, activity, meta) do
+    with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
+      PollWorker.schedule_poll_end(activity)
+      {:ok, object, meta}
+    end
+  end
+
+  def handle_object_creation(%{"type" => "Answer"} = object_map, _activity, meta) do
     with {:ok, object, meta} <- Pipeline.common_pipeline(object_map, meta) do
       Object.increase_vote_count(
         object.data["inReplyTo"],
@@ -436,15 +443,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
     end
   end
 
-  def handle_object_creation(%{"type" => objtype} = object, meta)
-      when objtype in ~w[Audio Video Question Event Article Note Page] do
+  def handle_object_creation(%{"type" => objtype} = object, _activity, meta)
+      when objtype in ~w[Audio Video Event Article Note Page] do
     with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
       {:ok, object, meta}
     end
   end
 
   # Nothing to do
-  def handle_object_creation(object, meta) do
+  def handle_object_creation(object, _activity, meta) do
     {:ok, object, meta}
   end
 
index 259068f048b6488b6e8f7aa5997ca1f968200531..345bc1e87de62ef0d32a84efa0b30fcffb423407 100644 (file)
@@ -13,7 +13,9 @@ defmodule Pleroma.Web.AdminAPI.Report do
     account = User.get_cached_by_ap_id(account_ap_id)
 
     statuses =
-      Enum.map(status_ap_ids, fn
+      status_ap_ids
+      |> Enum.reject(&is_nil(&1))
+      |> Enum.map(fn
         act when is_map(act) -> Activity.get_by_ap_id_with_object(act["id"])
         act when is_binary(act) -> Activity.get_by_ap_id_with_object(act)
       end)
index ec88eabe1f28d65f57bd590de6eaab77b811a2bc..e4ce42f1cf91540c2c0963950b0b255259551db9 100644 (file)
@@ -195,7 +195,8 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
         "pleroma:chat_mention",
         "pleroma:report",
         "move",
-        "follow_request"
+        "follow_request",
+        "poll"
       ],
       description: """
       The type of event that resulted in the notification.
index 0cafbc719cf8a6b9d9e0975233ce7fc1596d9ffe..879b2227ebd6401deac6cecec48742a40fd13599 100644 (file)
@@ -8,6 +8,8 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
   alias Pleroma.Web.ApiSpec.Schemas.ApiError
   alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
 
+  import Pleroma.Web.ApiSpec.Helpers
+
   def open_api_operation(action) do
     operation = String.to_existing_atom("#{action}_operation")
     apply(__MODULE__, operation, [])
@@ -63,17 +65,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
       summary: "Change account password",
       security: [%{"oAuth" => ["write:accounts"]}],
       operationId: "UtilController.change_password",
-      parameters: [
-        Operation.parameter(:password, :query, :string, "Current password", required: true),
-        Operation.parameter(:new_password, :query, :string, "New password", required: true),
-        Operation.parameter(
-          :new_password_confirmation,
-          :query,
-          :string,
-          "New password, confirmation",
-          required: true
-        )
-      ],
+      requestBody: request_body("Parameters", change_password_request(), required: true),
       responses: %{
         200 =>
           Operation.response("Success", "application/json", %Schema{
@@ -86,17 +78,30 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
     }
   end
 
+  defp change_password_request do
+    %Schema{
+      title: "ChangePasswordRequest",
+      description: "POST body for changing the account's passowrd",
+      type: :object,
+      required: [:password, :new_password, :new_password_confirmation],
+      properties: %{
+        password: %Schema{type: :string, description: "Current password"},
+        new_password: %Schema{type: :string, description: "New password"},
+        new_password_confirmation: %Schema{
+          type: :string,
+          description: "New password, confirmation"
+        }
+      }
+    }
+  end
+
   def change_email_operation do
     %Operation{
       tags: ["Account credentials"],
       summary: "Change account email",
       security: [%{"oAuth" => ["write:accounts"]}],
       operationId: "UtilController.change_email",
-      parameters: [
-        Operation.parameter(:password, :query, :string, "Current password", required: true),
-        Operation.parameter(:email, :query, :string, "New email", required: true)
-      ],
-      requestBody: nil,
+      requestBody: request_body("Parameters", change_email_request(), required: true),
       responses: %{
         200 =>
           Operation.response("Success", "application/json", %Schema{
@@ -109,6 +114,19 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
     }
   end
 
+  defp change_email_request do
+    %Schema{
+      title: "ChangeEmailRequest",
+      description: "POST body for changing the account's email",
+      type: :object,
+      required: [:email, :password],
+      properties: %{
+        email: %Schema{type: :string, description: "New email"},
+        password: %Schema{type: :string, description: "Current password"}
+      }
+    }
+  end
+
   def update_notificaton_settings_operation do
     %Operation{
       tags: ["Accounts"],
index 9f8a42606056d280a2811e90a11bde3daf69480a..b6feaf32a6304ac66a37690838cbeb531a27e85a 100644 (file)
@@ -385,19 +385,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
 
   def maybe_notify_mentioned_recipients(recipients, _), do: recipients
 
-  # Do not notify subscribers if author is making a reply
-  def maybe_notify_subscribers(recipients, %Activity{
-        object: %Object{data: %{"inReplyTo" => _ap_id}}
-      }) do
-    recipients
-  end
-
   def maybe_notify_subscribers(
         recipients,
-        %Activity{data: %{"actor" => actor, "type" => type}} = activity
-      )
-      when type == "Create" do
-    with %User{} = user <- User.get_cached_by_ap_id(actor) do
+        %Activity{data: %{"actor" => actor, "type" => "Create"}} = activity
+      ) do
+    # Do not notify subscribers if author is making a reply
+    with %Object{data: object} <- Object.normalize(activity, fetch: false),
+         nil <- object["inReplyTo"],
+         %User{} = user <- User.get_cached_by_ap_id(actor) do
       subscriber_ids =
         user
         |> User.subscriber_users()
index 647ba661e7b917151eb904e78a1644dedc0f9eb6..002d6b2cea48762b61eabc927cde7adfacc7da57 100644 (file)
@@ -50,6 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
     favourite
     move
     pleroma:emoji_reaction
+    poll
   }
   def index(%{assigns: %{user: user}} = conn, params) do
     params =
index 3528185d50345e102edc18686db70bde420cd08f..ef208062bdb121f12ecd77be6c5d5d38979d4587 100644 (file)
@@ -95,7 +95,20 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
       {:ok, data} = MRF.describe()
 
       data
-      |> Map.merge(%{quarantined_instances: quarantined})
+      |> Map.put(
+        :quarantined_instances,
+        Enum.map(quarantined, fn {instance, _reason} -> instance end)
+      )
+      # This is for backwards compatibility. We originally didn't sent
+      # extra info like a reason why an instance was rejected/quarantined/etc.
+      # Because we didn't want to break backwards compatibility it was decided
+      # to add an extra "info" key.
+      |> Map.put(:quarantined_instances_info, %{
+        "quarantined_instances" =>
+          quarantined
+          |> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end)
+          |> Map.new()
+      })
     else
       %{}
     end
index df9bedfed478b894a1b22bde6eb7912af7b3972b..35c636d4e8c24679ae5be3a5c87ecbd703d49272 100644 (file)
@@ -112,6 +112,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
       "move" ->
         put_target(response, activity, reading_user, %{})
 
+      "poll" ->
+        put_status(response, activity, reading_user, status_render_opts)
+
       "pleroma:emoji_reaction" ->
         response
         |> put_status(parent_activity_fn.(), reading_user, status_render_opts)
index da44e0a74a1918124f7e58b20e03e932a5d3963f..463f3419855f75d0c4960f09b43d9b3900c87abb 100644 (file)
@@ -65,11 +65,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
 
   defp get_context_id(_), do: nil
 
-  defp reblogged?(activity, user) do
-    object = Object.normalize(activity, fetch: false) || %{}
-    present?(user && user.ap_id in (object.data["announcements"] || []))
+  # Check if the user reblogged this status
+  defp reblogged?(activity, %User{ap_id: ap_id}) do
+    with %Object{data: %{"announcements" => announcements}} when is_list(announcements) <-
+           Object.normalize(activity, fetch: false) do
+      ap_id in announcements
+    else
+      _ -> false
+    end
   end
 
+  # False if the user is logged out
+  defp reblogged?(_activity, _user), do: false
+
   def render("index.json", opts) do
     reading_user = opts[:for]
 
diff --git a/lib/pleroma/web/plugs/user_is_staff_plug.ex b/lib/pleroma/web/plugs/user_is_staff_plug.ex
new file mode 100644 (file)
index 0000000..49c2d9c
--- /dev/null
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.UserIsStaffPlug do
+  import Pleroma.Web.TranslationHelpers
+  import Plug.Conn
+
+  alias Pleroma.User
+
+  def init(options) do
+    options
+  end
+
+  def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn
+  def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _), do: conn
+
+  def call(conn, _) do
+    conn
+    |> render_error(:forbidden, "User is not a staff member.")
+    |> halt()
+  end
+end
index 4f6c9bc9f731397794212ffb85103cc888cffe73..35bf2e2236dc61f382e5684ed3ef7946a9e9ff4e 100644 (file)
@@ -26,7 +26,7 @@ defmodule Pleroma.Web.Push.Subscription do
   end
 
   # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
-  @supported_alert_types ~w[follow favourite mention reblog pleroma:chat_mention pleroma:emoji_reaction]a
+  @supported_alert_types ~w[follow favourite mention reblog poll pleroma:chat_mention pleroma:emoji_reaction]a
 
   defp alerts(%{data: %{alerts: alerts}}) do
     alerts = Map.take(alerts, @supported_alert_types)
index efca7078a178344c091e17ac61f748bee68e65ba..74ee23c06c17d4108e1330de1b81fe9f56ca02ca 100644 (file)
@@ -96,10 +96,14 @@ defmodule Pleroma.Web.Router do
     plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug)
     plug(:after_auth)
     plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug)
-    plug(Pleroma.Web.Plugs.UserIsAdminPlug)
+    plug(Pleroma.Web.Plugs.UserIsStaffPlug)
     plug(Pleroma.Web.Plugs.IdempotencyPlug)
   end
 
+  pipeline :require_admin do
+    plug(Pleroma.Web.Plugs.UserIsAdminPlug)
+  end
+
   pipeline :mastodon_html do
     plug(:browser)
     plug(:authenticate)
@@ -160,7 +164,7 @@ defmodule Pleroma.Web.Router do
   end
 
   scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
-    pipe_through(:admin_api)
+    pipe_through([:admin_api, :require_admin])
 
     put("/users/disable_mfa", AdminAPIController, :disable_mfa)
     put("/users/tag", AdminAPIController, :tag_users)
@@ -265,7 +269,7 @@ defmodule Pleroma.Web.Router do
 
   scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
     scope "/pack" do
-      pipe_through(:admin_api)
+      pipe_through([:admin_api, :require_admin])
 
       post("/", EmojiPackController, :create)
       patch("/", EmojiPackController, :update)
@@ -280,7 +284,7 @@ defmodule Pleroma.Web.Router do
 
     # Modifying packs
     scope "/packs" do
-      pipe_through(:admin_api)
+      pipe_through([:admin_api, :require_admin])
 
       get("/import", EmojiPackController, :import_from_filesystem)
       get("/remote", EmojiPackController, :remote)
index a2e69666e46be00c27ce7d93a7a64984d13103d9..ef43f76820637cb8b44da7d423dc3ae288f4f4ed 100644 (file)
@@ -81,17 +81,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
     end
   end
 
-  def change_password(%{assigns: %{user: user}} = conn, %{
-        password: password,
-        new_password: new_password,
-        new_password_confirmation: new_password_confirmation
-      }) do
-    case CommonAPI.Utils.confirm_current_password(user, password) do
+  def change_password(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do
+    case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
       {:ok, user} ->
         with {:ok, _user} <-
                User.reset_password(user, %{
-                 password: new_password,
-                 password_confirmation: new_password_confirmation
+                 password: body_params.new_password,
+                 password_confirmation: body_params.new_password_confirmation
                }) do
           json(conn, %{status: "success"})
         else
@@ -108,10 +104,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
     end
   end
 
-  def change_email(%{assigns: %{user: user}} = conn, %{password: password, email: email}) do
-    case CommonAPI.Utils.confirm_current_password(user, password) do
+  def change_email(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do
+    case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
       {:ok, user} ->
-        with {:ok, _user} <- User.change_email(user, email) do
+        with {:ok, _user} <- User.change_email(user, body_params.email) do
           json(conn, %{status: "success"})
         else
           {:error, changeset} ->
diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex
new file mode 100644 (file)
index 0000000..3423cc8
--- /dev/null
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.PollWorker do
+  @moduledoc """
+  Generates notifications when a poll ends.
+  """
+  use Pleroma.Workers.WorkerHelper, queue: "poll_notifications"
+
+  alias Pleroma.Activity
+  alias Pleroma.Notification
+  alias Pleroma.Object
+
+  @impl Oban.Worker
+  def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do
+    with %Activity{} = activity <- find_poll_activity(activity_id) do
+      Notification.create_poll_notifications(activity)
+    end
+  end
+
+  defp find_poll_activity(activity_id) do
+    with nil <- Activity.get_by_id(activity_id) do
+      {:error, :poll_activity_not_found}
+    end
+  end
+
+  def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
+    with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <-
+           Object.normalize(activity),
+         {:ok, end_time} <- NaiveDateTime.from_iso8601(closed),
+         :gt <- NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) do
+      %{
+        op: "poll_end",
+        activity_id: activity_id
+      }
+      |> new(scheduled_at: end_time)
+      |> Oban.insert()
+    else
+      _ -> {:error, activity}
+    end
+  end
+
+  def schedule_poll_end(activity), do: {:error, activity}
+end
diff --git a/mix.exs b/mix.exs
index 763c580896016f0ae2cc43776efc12a02a1cf774..c33beb53372246495e63bcd1d817f22cb2ce0822 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
   def project do
     [
       app: :pleroma,
-      version: version("2.4.0"),
+      version: version("2.4.50"),
       elixir: "~> 1.9",
       elixirc_paths: elixirc_paths(Mix.env()),
       compilers: [:phoenix, :gettext] ++ Mix.compilers(),
index 653ea00a162077982760468a31587ba1826711f4..4d689902f1c18f00b30008f00a4fb626119a5512 100644 (file)
@@ -3,8 +3,8 @@ msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2020-05-13 16:37+0000\n"
-"PO-Revision-Date: 2020-07-09 14:40+0000\n"
-"Last-Translator: Ben Is <srsbzns@cock.li>\n"
+"PO-Revision-Date: 2021-08-13 15:42+0000\n"
+"Last-Translator: marcin mikołajczak <me@mkljczk.pl>\n"
 "Language-Team: Polish <https://translate.pleroma.social/projects/pleroma/"
 "pleroma/pl/>\n"
 "Language: pl\n"
@@ -13,7 +13,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "|| n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 4.0.4\n"
+"X-Generator: Weblate 4.6.2\n"
 
 ## This file is a PO Template file.
 ##
@@ -68,49 +68,49 @@ msgstr[2] "powinno mieć %{count} znaków"
 
 msgid "should have %{count} item(s)"
 msgid_plural "should have %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "powinno zawierać %{count} element"
+msgstr[1] "powinno zawierać %{count} elementy"
+msgstr[2] "powinno zawierać %{count} elementów"
 
 msgid "should be at least %{count} character(s)"
 msgid_plural "should be at least %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "powinno zawierać przynajmniej %{count} znak"
+msgstr[1] "powinno zawierać przynajmniej %{count} znaki"
+msgstr[2] "powinno zawierać przynajmniej %{count} znaków"
 
 msgid "should have at least %{count} item(s)"
 msgid_plural "should have at least %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "powinno zawierać przynajmniej %{count} element"
+msgstr[1] "powinno zawierać przynajmniej %{count} elementy"
+msgstr[2] "powinno zawierać przynajmniej %{count} elementów"
 
 msgid "should be at most %{count} character(s)"
 msgid_plural "should be at most %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "powinno zawierać najwyżej %{count} znak"
+msgstr[1] "powinno zawierać najwyżej %{count} znaki"
+msgstr[2] "powinno zawierać najwyżej %{count} znaków"
 
 msgid "should have at most %{count} item(s)"
 msgid_plural "should have at most %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "powinno zawierać najwyżej %{count} element"
+msgstr[1] "powinno zawierać najwyżej %{count} elementy"
+msgstr[2] "powinno zawierać najwyżej %{count} elementów"
 
 ## From Ecto.Changeset.validate_number/3
 msgid "must be less than %{number}"
-msgstr ""
+msgstr "musi wynosić mniej niż %{number}"
 
 msgid "must be greater than %{number}"
-msgstr ""
+msgstr "musi wynosić więcej niż %{number}"
 
 msgid "must be less than or equal to %{number}"
-msgstr ""
+msgstr "musi być mniejsze lub równe %{number}"
 
 msgid "must be greater than or equal to %{number}"
-msgstr ""
+msgstr "musi być większe lub równe %{number}"
 
 msgid "must be equal to %{number}"
-msgstr ""
+msgstr "musi być równe %{number}"
 
 #: lib/pleroma/web/common_api/common_api.ex:421
 #, elixir-format
@@ -152,7 +152,7 @@ msgstr "Nie znaleziono użytkownika"
 #: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
 #, elixir-format
 msgid "Can't get favorites"
-msgstr ""
+msgstr "Nie można uzyskać ulubionych"
 
 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
 #, elixir-format
@@ -172,7 +172,7 @@ msgstr "Komentarz może mieć co najwyżej %{max_size} znaków"
 #: lib/pleroma/config/config_db.ex:222
 #, elixir-format
 msgid "Config with params %{params} not found"
-msgstr ""
+msgstr "Nie znaleziono konfiguracji z parametrami %{params}"
 
 #: lib/pleroma/web/common_api/common_api.ex:95
 #, elixir-format
@@ -213,38 +213,38 @@ msgstr "Nie udało się cofnąć powtórzenia"
 #: lib/pleroma/web/common_api/common_api.ex:437
 #, elixir-format
 msgid "Could not update state"
-msgstr ""
+msgstr "Nie można zaktualizować stanu"
 
 #: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:202
 #, elixir-format
 msgid "Error."
-msgstr ""
+msgstr "Błąd."
 
 #: lib/pleroma/web/twitter_api/twitter_api.ex:106
 #, elixir-format
 msgid "Invalid CAPTCHA"
-msgstr ""
+msgstr "Niewłaściwa CAPTCHA"
 
 #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:117
 #: lib/pleroma/web/oauth/oauth_controller.ex:569
 #, elixir-format
 msgid "Invalid credentials"
-msgstr ""
+msgstr "Nieprawidłowe dane uwierzytelniania"
 
 #: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
 #, elixir-format
 msgid "Invalid credentials."
-msgstr ""
+msgstr "Nieprawidłowe dane uwierzytelniania."
 
 #: lib/pleroma/web/common_api/common_api.ex:265
 #, elixir-format
 msgid "Invalid indices"
-msgstr ""
+msgstr "Nieprawidłowe indeksy"
 
 #: lib/pleroma/web/admin_api/admin_api_controller.ex:1147
 #, elixir-format
 msgid "Invalid parameters"
-msgstr ""
+msgstr "Nieprawidłowe parametry"
 
 #: lib/pleroma/web/common_api/utils.ex:411
 #, elixir-format
@@ -307,7 +307,7 @@ msgstr "Coś się zepsuło"
 #: lib/pleroma/web/common_api/activity_draft.ex:107
 #, elixir-format
 msgid "The message visibility must be direct"
-msgstr ""
+msgstr "Widoczność wiadomości musi być „Bezpośrednia”"
 
 #: lib/pleroma/web/common_api/utils.ex:566
 #, elixir-format
@@ -317,17 +317,17 @@ msgstr "Ten status przekracza limit znaków"
 #: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
 #, elixir-format
 msgid "This resource requires authentication."
-msgstr ""
+msgstr "Ten zasób wymaga uwierzytelnienia."
 
 #: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
 #, elixir-format
 msgid "Throttled"
-msgstr ""
+msgstr "Ograniczono"
 
 #: lib/pleroma/web/common_api/common_api.ex:266
 #, elixir-format
 msgid "Too many choices"
-msgstr ""
+msgstr "Zbyt wiele wyborów"
 
 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:442
 #, elixir-format
@@ -349,17 +349,18 @@ msgstr "Twoje konto jest obecnie nieaktywne"
 #: lib/pleroma/web/oauth/oauth_controller.ex:332
 #, elixir-format
 msgid "Your login is missing a confirmed e-mail address"
-msgstr ""
+msgstr "Twój adres e-mail nie został potwierdzony"
 
 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:389
 #, elixir-format
 msgid "can't read inbox of %{nickname} as %{as_nickname}"
-msgstr ""
+msgstr "Nie można odczytać skrzynki odbiorczej %{nickname} jako %{as_nickname}"
 
 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:472
 #, elixir-format
 msgid "can't update outbox of %{nickname} as %{as_nickname}"
 msgstr ""
+"Nie można zaktualizować skrzynki nadawczcej %{nickname} jako %{as_nickname}"
 
 #: lib/pleroma/web/common_api/common_api.ex:388
 #, elixir-format
@@ -405,12 +406,12 @@ msgstr "Nie udało się"
 #: lib/pleroma/web/oauth/oauth_controller.ex:411
 #, elixir-format
 msgid "Failed to authenticate: %{message}."
-msgstr ""
+msgstr "Nie udało się uwierzytelnić: %{message}."
 
 #: lib/pleroma/web/oauth/oauth_controller.ex:442
 #, elixir-format
 msgid "Failed to set up user account."
-msgstr ""
+msgstr "Nie udało się skonfigurować konta użytkownika."
 
 #: lib/pleroma/plugs/oauth_scopes_plug.ex:38
 #, elixir-format
@@ -431,7 +432,7 @@ msgstr "Nieprawidłowa nazwa użytkownika lub hasło"
 #: lib/pleroma/web/twitter_api/twitter_api.ex:118
 #, elixir-format
 msgid "Invalid answer data"
-msgstr ""
+msgstr "Nieprawidłowe dane odpowiedzi"
 
 #: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:128
 #, elixir-format
@@ -441,7 +442,7 @@ msgstr "Nieobsługiwana wersja schematu Nodeinfo"
 #: lib/pleroma/web/oauth/oauth_controller.ex:169
 #, elixir-format
 msgid "This action is outside the authorized scopes"
-msgstr ""
+msgstr "Ta akcja wykracza poza dozwolone zakresy"
 
 #: lib/pleroma/web/oauth/fallback_controller.ex:14
 #, elixir-format
@@ -477,12 +478,12 @@ msgstr "Błąd CAPTCHA"
 #: lib/pleroma/web/common_api/common_api.ex:200
 #, elixir-format
 msgid "Could not add reaction emoji"
-msgstr ""
+msgstr "Nie można dodać reakcji emoji"
 
 #: lib/pleroma/web/common_api/common_api.ex:211
 #, elixir-format
 msgid "Could not remove reaction emoji"
-msgstr ""
+msgstr "Nie można usunąć reakcji emoji"
 
 #: lib/pleroma/web/twitter_api/twitter_api.ex:129
 #, elixir-format
@@ -535,6 +536,8 @@ msgstr "Wymagany reset hasła"
 #, elixir-format
 msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
 msgstr ""
+"Naruszenie bezpieczeństwa: sprawdzanie zakresów OAuth nie zostało ani "
+"wykonane, ani celowo pominięte."
 
 #: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
 #, elixir-format
@@ -569,7 +572,7 @@ msgstr "Nieoczekiwany błąd podczas zmieniania metadanych paczki."
 #: lib/pleroma/plugs/user_is_admin_plug.ex:21
 #, elixir-format
 msgid "User is not an admin."
-msgstr ""
+msgstr "Użytkownik nie jest administratorem."
 
 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
 #, elixir-format
diff --git a/priv/repo/migrations/20201005123100_simple_policy_string_to_tuple.exs b/priv/repo/migrations/20201005123100_simple_policy_string_to_tuple.exs
new file mode 100644 (file)
index 0000000..77a4a73
--- /dev/null
@@ -0,0 +1,40 @@
+defmodule Pleroma.Repo.Migrations.SimplePolicyStringToTuple do
+  use Ecto.Migration
+
+  alias Pleroma.ConfigDB
+
+  def up, do: ConfigDB.get_by_params(%{group: :pleroma, key: :mrf_simple}) |> update_to_tuples
+  def down, do: ConfigDB.get_by_params(%{group: :pleroma, key: :mrf_simple}) |> update_to_strings
+
+  defp update_to_tuples(%{value: value}) do
+    new_value =
+      value
+      |> Enum.map(fn {k, v} ->
+        {k,
+         Enum.map(v, fn
+           {instance, reason} -> {instance, reason}
+           instance -> {instance, ""}
+         end)}
+      end)
+
+    ConfigDB.update_or_create(%{group: :pleroma, key: :mrf_simple, value: new_value})
+  end
+
+  defp update_to_tuples(nil), do: {:ok, nil}
+
+  defp update_to_strings(%{value: value}) do
+    new_value =
+      value
+      |> Enum.map(fn {k, v} ->
+        {k,
+         Enum.map(v, fn
+           {instance, _} -> instance
+           instance -> instance
+         end)}
+      end)
+
+    ConfigDB.update_or_create(%{group: :pleroma, key: :mrf_simple, value: new_value})
+  end
+
+  defp update_to_strings(nil), do: {:ok, nil}
+end
diff --git a/priv/repo/migrations/20201005124600_quarantained_policy_string_to_tuple.exs b/priv/repo/migrations/20201005124600_quarantained_policy_string_to_tuple.exs
new file mode 100644 (file)
index 0000000..b924e46
--- /dev/null
@@ -0,0 +1,61 @@
+defmodule Pleroma.Repo.Migrations.QuarantainedStringToTuple do
+  use Ecto.Migration
+
+  alias Pleroma.ConfigDB
+
+  def up,
+    do:
+      ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      |> update_quarantined_instances_to_tuples
+
+  def down,
+    do:
+      ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      |> update_quarantined_instances_to_strings
+
+  defp update_quarantined_instances_to_tuples(%{value: settings}) do
+    settings |> List.keyfind(:quarantined_instances, 0) |> update_to_tuples
+  end
+
+  defp update_quarantined_instances_to_tuples(nil), do: {:ok, nil}
+
+  defp update_to_tuples({:quarantined_instances, instance_list}) do
+    new_value =
+      instance_list
+      |> Enum.map(fn
+        {v, r} -> {v, r}
+        v -> {v, ""}
+      end)
+
+    ConfigDB.update_or_create(%{
+      group: :pleroma,
+      key: :instance,
+      value: [quarantined_instances: new_value]
+    })
+  end
+
+  defp update_to_tuples(nil), do: {:ok, nil}
+
+  defp update_quarantined_instances_to_strings(%{value: settings}) do
+    settings |> List.keyfind(:quarantined_instances, 0) |> update_to_strings
+  end
+
+  defp update_quarantined_instances_to_strings(nil), do: {:ok, nil}
+
+  defp update_to_strings({:quarantined_instances, instance_list}) do
+    new_value =
+      instance_list
+      |> Enum.map(fn
+        {v, _} -> v
+        v -> v
+      end)
+
+    ConfigDB.update_or_create(%{
+      group: :pleroma,
+      key: :instance,
+      value: [quarantined_instances: new_value]
+    })
+  end
+
+  defp update_to_strings(nil), do: {:ok, nil}
+end
diff --git a/priv/repo/migrations/20201005132900_transparency_exclusions_string_to_tuple.exs b/priv/repo/migrations/20201005132900_transparency_exclusions_string_to_tuple.exs
new file mode 100644 (file)
index 0000000..6516083
--- /dev/null
@@ -0,0 +1,61 @@
+defmodule Pleroma.Repo.Migrations.TransparencyExclusionsStringToTuple do
+  use Ecto.Migration
+
+  alias Pleroma.ConfigDB
+
+  def up,
+    do:
+      ConfigDB.get_by_params(%{group: :pleroma, key: :mrf})
+      |> update_transparency_exclusions_instances_to_tuples
+
+  def down,
+    do:
+      ConfigDB.get_by_params(%{group: :pleroma, key: :mrf})
+      |> update_transparency_exclusions_instances_to_strings
+
+  defp update_transparency_exclusions_instances_to_tuples(%{value: settings}) do
+    settings |> List.keyfind(:transparency_exclusions, 0) |> update_to_tuples
+  end
+
+  defp update_transparency_exclusions_instances_to_tuples(nil), do: {:ok, nil}
+
+  defp update_to_tuples({:transparency_exclusions, instance_list}) do
+    new_value =
+      instance_list
+      |> Enum.map(fn
+        {v, r} -> {v, r}
+        v -> {v, ""}
+      end)
+
+    ConfigDB.update_or_create(%{
+      group: :pleroma,
+      key: :mrf,
+      value: [transparency_exclusions: new_value]
+    })
+  end
+
+  defp update_to_tuples(nil), do: {:ok, nil}
+
+  defp update_transparency_exclusions_instances_to_strings(%{value: settings}) do
+    settings |> List.keyfind(:transparency_exclusions, 0) |> update_to_strings
+  end
+
+  defp update_transparency_exclusions_instances_to_strings(nil), do: {:ok, nil}
+
+  defp update_to_strings({:transparency_exclusions, instance_list}) do
+    new_value =
+      instance_list
+      |> Enum.map(fn
+        {v, _} -> v
+        v -> v
+      end)
+
+    ConfigDB.update_or_create(%{
+      group: :pleroma,
+      key: :mrf,
+      value: [transparency_exclusions: new_value]
+    })
+  end
+
+  defp update_to_strings(nil), do: {:ok, nil}
+end
diff --git a/priv/repo/migrations/20210717000000_add_poll_to_notifications_enum.exs b/priv/repo/migrations/20210717000000_add_poll_to_notifications_enum.exs
new file mode 100644 (file)
index 0000000..9abf40b
--- /dev/null
@@ -0,0 +1,49 @@
+defmodule Pleroma.Repo.Migrations.AddPollToNotificationsEnum do
+  use Ecto.Migration
+
+  @disable_ddl_transaction true
+
+  def up do
+    """
+    alter type notification_type add value 'poll'
+    """
+    |> execute()
+  end
+
+  def down do
+    alter table(:notifications) do
+      modify(:type, :string)
+    end
+
+    """
+    delete from notifications where type = 'poll'
+    """
+    |> execute()
+
+    """
+    drop type if exists notification_type
+    """
+    |> execute()
+
+    """
+    create type notification_type as enum (
+      'follow',
+      'follow_request',
+      'mention',
+      'move',
+      'pleroma:emoji_reaction',
+      'pleroma:chat_mention',
+      'reblog',
+      'favourite',
+      'pleroma:report'
+    )
+    """
+    |> execute()
+
+    """
+    alter table notifications
+    alter column type type notification_type using (type::notification_type)
+    """
+    |> execute()
+  end
+end
index ccf86634f0b593f5de77d1be6420f54afcb6f66d..c5e2b20f4d1628083ed50debc0d06a2085ff6897 100644 (file)
@@ -11,6 +11,183 @@ defmodule Pleroma.Config.DeprecationWarningsTest do
   alias Pleroma.Config
   alias Pleroma.Config.DeprecationWarnings
 
+  describe "simple policy tuples" do
+    test "gives warning when there are still strings" do
+      clear_config([:mrf_simple],
+        media_removal: ["some.removal"],
+        media_nsfw: ["some.nsfw"],
+        federated_timeline_removal: ["some.tl.removal"],
+        report_removal: ["some.report.removal"],
+        reject: ["some.reject"],
+        followers_only: ["some.followers.only"],
+        accept: ["some.accept"],
+        avatar_removal: ["some.avatar.removal"],
+        banner_removal: ["some.banner.removal"],
+        reject_deletes: ["some.reject.deletes"]
+      )
+
+      assert capture_log(fn -> DeprecationWarnings.check_simple_policy_tuples() end) =~
+               """
+               !!!DEPRECATION WARNING!!!
+               Your config is using strings in the SimplePolicy configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+               ```
+               config :pleroma, :mrf_simple,
+                 media_removal: ["instance.tld"],
+                 media_nsfw: ["instance.tld"],
+                 federated_timeline_removal: ["instance.tld"],
+                 report_removal: ["instance.tld"],
+                 reject: ["instance.tld"],
+                 followers_only: ["instance.tld"],
+                 accept: ["instance.tld"],
+                 avatar_removal: ["instance.tld"],
+                 banner_removal: ["instance.tld"],
+                 reject_deletes: ["instance.tld"]
+               ```
+
+               Is now
+
+
+               ```
+               config :pleroma, :mrf_simple,
+                 media_removal: [{"instance.tld", "Reason for media removal"}],
+                 media_nsfw: [{"instance.tld", "Reason for media nsfw"}],
+                 federated_timeline_removal: [{"instance.tld", "Reason for federated timeline removal"}],
+                 report_removal: [{"instance.tld", "Reason for report removal"}],
+                 reject: [{"instance.tld", "Reason for reject"}],
+                 followers_only: [{"instance.tld", "Reason for followers only"}],
+                 accept: [{"instance.tld", "Reason for accept"}],
+                 avatar_removal: [{"instance.tld", "Reason for avatar removal"}],
+                 banner_removal: [{"instance.tld", "Reason for banner removal"}],
+                 reject_deletes: [{"instance.tld", "Reason for reject deletes"}]
+               ```
+               """
+    end
+
+    test "transforms config to tuples" do
+      clear_config([:mrf_simple],
+        media_removal: ["some.removal", {"some.other.instance", "Some reason"}]
+      )
+
+      expected_config = [
+        {:media_removal, [{"some.removal", ""}, {"some.other.instance", "Some reason"}]}
+      ]
+
+      capture_log(fn -> DeprecationWarnings.warn() end)
+
+      assert Config.get([:mrf_simple]) == expected_config
+    end
+
+    test "doesn't give a warning with correct config" do
+      clear_config([:mrf_simple],
+        media_removal: [{"some.removal", ""}, {"some.other.instance", "Some reason"}]
+      )
+
+      assert capture_log(fn -> DeprecationWarnings.check_simple_policy_tuples() end) == ""
+    end
+  end
+
+  describe "quarantined_instances tuples" do
+    test "gives warning when there are still strings" do
+      clear_config([:instance, :quarantined_instances], [
+        {"domain.com", "some reason"},
+        "somedomain.tld"
+      ])
+
+      assert capture_log(fn -> DeprecationWarnings.check_quarantined_instances_tuples() end) =~
+               """
+               !!!DEPRECATION WARNING!!!
+               Your config is using strings in the quarantined_instances configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+               ```
+               config :pleroma, :instance,
+                 quarantined_instances: ["instance.tld"]
+               ```
+
+               Is now
+
+
+               ```
+               config :pleroma, :instance,
+                 quarantined_instances: [{"instance.tld", "Reason for quarantine"}]
+               ```
+               """
+    end
+
+    test "transforms config to tuples" do
+      clear_config([:instance, :quarantined_instances], [
+        {"domain.com", "some reason"},
+        "some.tld"
+      ])
+
+      expected_config = [{"domain.com", "some reason"}, {"some.tld", ""}]
+
+      capture_log(fn -> DeprecationWarnings.warn() end)
+
+      assert Config.get([:instance, :quarantined_instances]) == expected_config
+    end
+
+    test "doesn't give a warning with correct config" do
+      clear_config([:instance, :quarantined_instances], [
+        {"domain.com", "some reason"},
+        {"some.tld", ""}
+      ])
+
+      assert capture_log(fn -> DeprecationWarnings.check_quarantined_instances_tuples() end) == ""
+    end
+  end
+
+  describe "transparency_exclusions tuples" do
+    test "gives warning when there are still strings" do
+      clear_config([:mrf, :transparency_exclusions], [
+        {"domain.com", "some reason"},
+        "somedomain.tld"
+      ])
+
+      assert capture_log(fn -> DeprecationWarnings.check_transparency_exclusions_tuples() end) =~
+               """
+               !!!DEPRECATION WARNING!!!
+               Your config is using strings in the transparency_exclusions configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+               ```
+               config :pleroma, :mrf,
+                 transparency_exclusions: ["instance.tld"]
+               ```
+
+               Is now
+
+
+               ```
+               config :pleroma, :mrf,
+                 transparency_exclusions: [{"instance.tld", "Reason to exlude transparency"}]
+               ```
+               """
+    end
+
+    test "transforms config to tuples" do
+      clear_config([:mrf, :transparency_exclusions], [
+        {"domain.com", "some reason"},
+        "some.tld"
+      ])
+
+      expected_config = [{"domain.com", "some reason"}, {"some.tld", ""}]
+
+      capture_log(fn -> DeprecationWarnings.warn() end)
+
+      assert Config.get([:mrf, :transparency_exclusions]) == expected_config
+    end
+
+    test "doesn't give a warning with correct config" do
+      clear_config([:mrf, :transparency_exclusions], [
+        {"domain.com", "some reason"},
+        {"some.tld", ""}
+      ])
+
+      assert capture_log(fn -> DeprecationWarnings.check_transparency_exclusions_tuples() end) ==
+               ""
+    end
+  end
+
   test "check_old_mrf_config/0" do
     clear_config([:instance, :rewrite_policy], [])
     clear_config([:instance, :mrf_transparency], true)
index 85f895f0fab7fa8973822d3429c51522c59063ce..716af496d8074a6e1dd25098c7e8c03fdfd18cfb 100644 (file)
@@ -129,6 +129,19 @@ defmodule Pleroma.NotificationTest do
     end
   end
 
+  test "create_poll_notifications/1" do
+    [user1, user2, user3, _, _] = insert_list(5, :user)
+    question = insert(:question, user: user1)
+    activity = insert(:question_activity, question: question)
+
+    {:ok, _, _} = CommonAPI.vote(user2, question, [0])
+    {:ok, _, _} = CommonAPI.vote(user3, question, [1])
+
+    {:ok, notifications} = Notification.create_poll_notifications(activity)
+
+    assert [user2.id, user3.id, user1.id] == Enum.map(notifications, & &1.user_id)
+  end
+
   describe "CommonApi.post/2 notification-related functionality" do
     test_with_mock "creates but does NOT send notification to blocker user",
                    Push,
index 4021a565da63258b6a480ef06cb7119d92f0fea6..c2ed2c2a3f2b4d47ed07713d72ddaed29beabf97 100644 (file)
@@ -480,7 +480,7 @@ defmodule Pleroma.UserTest do
             )
 
     test "it sends a welcome chat message when Simple policy applied to local instance" do
-      clear_config([:mrf_simple, :media_nsfw], ["localhost"])
+      clear_config([:mrf_simple, :media_nsfw], [{"localhost", ""}])
 
       welcome_user = insert(:user)
       clear_config([:welcome, :chat_message, :enabled], true)
index 137aafd39ed62d2139ac7aec2baae17e0ea21421..2f649a0a4818e731fe7cc04944709331d14c261d 100644 (file)
@@ -22,6 +22,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do
   defp get_old_message do
     File.read!("test/fixtures/mastodon-post-activity.json")
     |> Jason.decode!()
+    |> Map.drop(["published"])
   end
 
   defp get_new_message do
index 0b0143d09435386e792bf6a2cedf1990923b1579..0a0f51bdbb8c91bf27dc903f216579414e900887 100644 (file)
@@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "has a matching host" do
-      clear_config([:mrf_simple, :media_removal], ["remote.instance"])
+      clear_config([:mrf_simple, :media_removal], [{"remote.instance", "Some reason"}])
       media_message = build_media_message()
       local_message = build_local_message()
 
@@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "match with wildcard domain" do
-      clear_config([:mrf_simple, :media_removal], ["*.remote.instance"])
+      clear_config([:mrf_simple, :media_removal], [{"*.remote.instance", "Whatever reason"}])
       media_message = build_media_message()
       local_message = build_local_message()
 
@@ -70,7 +70,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "has a matching host" do
-      clear_config([:mrf_simple, :media_nsfw], ["remote.instance"])
+      clear_config([:mrf_simple, :media_nsfw], [{"remote.instance", "Whetever"}])
       media_message = build_media_message()
       local_message = build_local_message()
 
@@ -81,7 +81,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "match with wildcard domain" do
-      clear_config([:mrf_simple, :media_nsfw], ["*.remote.instance"])
+      clear_config([:mrf_simple, :media_nsfw], [{"*.remote.instance", "yeah yeah"}])
       media_message = build_media_message()
       local_message = build_local_message()
 
@@ -115,7 +115,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "has a matching host" do
-      clear_config([:mrf_simple, :report_removal], ["remote.instance"])
+      clear_config([:mrf_simple, :report_removal], [{"remote.instance", "muh"}])
       report_message = build_report_message()
       local_message = build_local_message()
 
@@ -124,7 +124,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "match with wildcard domain" do
-      clear_config([:mrf_simple, :report_removal], ["*.remote.instance"])
+      clear_config([:mrf_simple, :report_removal], [{"*.remote.instance", "suya"}])
       report_message = build_report_message()
       local_message = build_local_message()
 
@@ -159,7 +159,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
         |> URI.parse()
         |> Map.fetch!(:host)
 
-      clear_config([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host])
+      clear_config([:mrf_simple, :federated_timeline_removal], [{ftl_message_actor_host, "uwu"}])
       local_message = build_local_message()
 
       assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
@@ -180,7 +180,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
         |> URI.parse()
         |> Map.fetch!(:host)
 
-      clear_config([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host])
+      clear_config([:mrf_simple, :federated_timeline_removal], [
+        {"*." <> ftl_message_actor_host, "owo"}
+      ])
+
       local_message = build_local_message()
 
       assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
@@ -203,7 +206,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
 
       ftl_message = Map.put(ftl_message, "cc", [])
 
-      clear_config([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host])
+      clear_config([:mrf_simple, :federated_timeline_removal], [
+        {ftl_message_actor_host, "spiderwaifu goes 88w88"}
+      ])
 
       assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
       refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
@@ -232,7 +237,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "activity has a matching host" do
-      clear_config([:mrf_simple, :reject], ["remote.instance"])
+      clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
 
       remote_message = build_remote_message()
 
@@ -240,7 +245,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "activity matches with wildcard domain" do
-      clear_config([:mrf_simple, :reject], ["*.remote.instance"])
+      clear_config([:mrf_simple, :reject], [{"*.remote.instance", ""}])
 
       remote_message = build_remote_message()
 
@@ -248,7 +253,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "actor has a matching host" do
-      clear_config([:mrf_simple, :reject], ["remote.instance"])
+      clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
 
       remote_user = build_remote_user()
 
@@ -256,7 +261,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "reject Announce when object would be rejected" do
-      clear_config([:mrf_simple, :reject], ["blocked.tld"])
+      clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
 
       announce = %{
         "type" => "Announce",
@@ -268,7 +273,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "reject by URI object" do
-      clear_config([:mrf_simple, :reject], ["blocked.tld"])
+      clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
 
       announce = %{
         "type" => "Announce",
@@ -322,7 +327,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
         |> URI.parse()
         |> Map.fetch!(:host)
 
-      clear_config([:mrf_simple, :followers_only], [actor_domain])
+      clear_config([:mrf_simple, :followers_only], [{actor_domain, ""}])
 
       assert {:ok, new_activity} = SimplePolicy.filter(activity)
       assert actor.follower_address in new_activity["cc"]
@@ -350,7 +355,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "is not empty but activity doesn't have a matching host" do
-      clear_config([:mrf_simple, :accept], ["non.matching.remote"])
+      clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
 
       local_message = build_local_message()
       remote_message = build_remote_message()
@@ -360,7 +365,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "activity has a matching host" do
-      clear_config([:mrf_simple, :accept], ["remote.instance"])
+      clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
 
       local_message = build_local_message()
       remote_message = build_remote_message()
@@ -370,7 +375,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "activity matches with wildcard domain" do
-      clear_config([:mrf_simple, :accept], ["*.remote.instance"])
+      clear_config([:mrf_simple, :accept], [{"*.remote.instance", ""}])
 
       local_message = build_local_message()
       remote_message = build_remote_message()
@@ -380,7 +385,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "actor has a matching host" do
-      clear_config([:mrf_simple, :accept], ["remote.instance"])
+      clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
 
       remote_user = build_remote_user()
 
@@ -398,7 +403,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "is not empty but it doesn't have a matching host" do
-      clear_config([:mrf_simple, :avatar_removal], ["non.matching.remote"])
+      clear_config([:mrf_simple, :avatar_removal], [{"non.matching.remote", ""}])
 
       remote_user = build_remote_user()
 
@@ -406,7 +411,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "has a matching host" do
-      clear_config([:mrf_simple, :avatar_removal], ["remote.instance"])
+      clear_config([:mrf_simple, :avatar_removal], [{"remote.instance", ""}])
 
       remote_user = build_remote_user()
       {:ok, filtered} = SimplePolicy.filter(remote_user)
@@ -415,7 +420,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "match with wildcard domain" do
-      clear_config([:mrf_simple, :avatar_removal], ["*.remote.instance"])
+      clear_config([:mrf_simple, :avatar_removal], [{"*.remote.instance", ""}])
 
       remote_user = build_remote_user()
       {:ok, filtered} = SimplePolicy.filter(remote_user)
@@ -434,7 +439,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "is not empty but it doesn't have a matching host" do
-      clear_config([:mrf_simple, :banner_removal], ["non.matching.remote"])
+      clear_config([:mrf_simple, :banner_removal], [{"non.matching.remote", ""}])
 
       remote_user = build_remote_user()
 
@@ -442,7 +447,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "has a matching host" do
-      clear_config([:mrf_simple, :banner_removal], ["remote.instance"])
+      clear_config([:mrf_simple, :banner_removal], [{"remote.instance", ""}])
 
       remote_user = build_remote_user()
       {:ok, filtered} = SimplePolicy.filter(remote_user)
@@ -451,7 +456,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "match with wildcard domain" do
-      clear_config([:mrf_simple, :banner_removal], ["*.remote.instance"])
+      clear_config([:mrf_simple, :banner_removal], [{"*.remote.instance", ""}])
 
       remote_user = build_remote_user()
       {:ok, filtered} = SimplePolicy.filter(remote_user)
@@ -464,7 +469,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     setup do: clear_config([:mrf_simple, :reject_deletes], [])
 
     test "it accepts deletions even from rejected servers" do
-      clear_config([:mrf_simple, :reject], ["remote.instance"])
+      clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
 
       deletion_message = build_remote_deletion_message()
 
@@ -472,7 +477,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "it accepts deletions even from non-whitelisted servers" do
-      clear_config([:mrf_simple, :accept], ["non.matching.remote"])
+      clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
 
       deletion_message = build_remote_deletion_message()
 
@@ -481,10 +486,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
   end
 
   describe "when :reject_deletes is not empty but it doesn't have a matching host" do
-    setup do: clear_config([:mrf_simple, :reject_deletes], ["non.matching.remote"])
+    setup do: clear_config([:mrf_simple, :reject_deletes], [{"non.matching.remote", ""}])
 
     test "it accepts deletions even from rejected servers" do
-      clear_config([:mrf_simple, :reject], ["remote.instance"])
+      clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
 
       deletion_message = build_remote_deletion_message()
 
@@ -492,7 +497,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     end
 
     test "it accepts deletions even from non-whitelisted servers" do
-      clear_config([:mrf_simple, :accept], ["non.matching.remote"])
+      clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
 
       deletion_message = build_remote_deletion_message()
 
@@ -501,7 +506,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
   end
 
   describe "when :reject_deletes has a matching host" do
-    setup do: clear_config([:mrf_simple, :reject_deletes], ["remote.instance"])
+    setup do: clear_config([:mrf_simple, :reject_deletes], [{"remote.instance", ""}])
 
     test "it rejects the deletion" do
       deletion_message = build_remote_deletion_message()
@@ -511,7 +516,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
   end
 
   describe "when :reject_deletes match with wildcard domain" do
-    setup do: clear_config([:mrf_simple, :reject_deletes], ["*.remote.instance"])
+    setup do: clear_config([:mrf_simple, :reject_deletes], [{"*.remote.instance", ""}])
 
     test "it rejects the deletion" do
       deletion_message = build_remote_deletion_message()
index 61d308b978a20d16d7e8dd5905e2dcc81391a13a..6ab27bc8676f91d9c4bc234fbae7c1240e99776f 100644 (file)
@@ -63,6 +63,15 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
     end
   end
 
+  describe "instance_list_from_tuples/1" do
+    test "returns a list of instances from a list of {instance, reason} tuples" do
+      list = [{"some.tld", "a reason"}, {"other.tld", "another reason"}]
+      expected = ["some.tld", "other.tld"]
+
+      assert MRF.instance_list_from_tuples(list) == expected
+    end
+  end
+
   describe "describe/0" do
     test "it works as expected with noop policy" do
       clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy])
index 89f3ad411d1b5f7a10d3c0575991be6c3a55762c..b50e22bbe794ccf24733b5ab404849ae3793b1e9 100644 (file)
@@ -267,6 +267,80 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
   end
 
   describe "publish/2" do
+    test_with_mock "doesn't publish a non-public activity to quarantined instances.",
+                   Pleroma.Web.Federator.Publisher,
+                   [:passthrough],
+                   [] do
+      Config.put([:instance, :quarantined_instances], [{"domain.com", "some reason"}])
+
+      follower =
+        insert(:user, %{
+          local: false,
+          inbox: "https://domain.com/users/nick1/inbox",
+          ap_enabled: true
+        })
+
+      actor = insert(:user, follower_address: follower.ap_id)
+
+      {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
+      actor = refresh_record(actor)
+
+      note_activity =
+        insert(:followers_only_note_activity,
+          user: actor,
+          recipients: [follower.ap_id]
+        )
+
+      res = Publisher.publish(actor, note_activity)
+
+      assert res == :ok
+
+      assert not called(
+               Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
+                 inbox: "https://domain.com/users/nick1/inbox",
+                 actor_id: actor.id,
+                 id: note_activity.data["id"]
+               })
+             )
+    end
+
+    test_with_mock "Publishes a non-public activity to non-quarantined instances.",
+                   Pleroma.Web.Federator.Publisher,
+                   [:passthrough],
+                   [] do
+      Config.put([:instance, :quarantined_instances], [{"somedomain.com", "some reason"}])
+
+      follower =
+        insert(:user, %{
+          local: false,
+          inbox: "https://domain.com/users/nick1/inbox",
+          ap_enabled: true
+        })
+
+      actor = insert(:user, follower_address: follower.ap_id)
+
+      {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
+      actor = refresh_record(actor)
+
+      note_activity =
+        insert(:followers_only_note_activity,
+          user: actor,
+          recipients: [follower.ap_id]
+        )
+
+      res = Publisher.publish(actor, note_activity)
+
+      assert res == :ok
+
+      assert called(
+               Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
+                 inbox: "https://domain.com/users/nick1/inbox",
+                 actor_id: actor.id,
+                 id: note_activity.data["id"]
+               })
+             )
+    end
+
     test_with_mock "publishes an activity with BCC to all relevant peers.",
                    Pleroma.Web.Federator.Publisher,
                    [:passthrough],
index 13167f50a8405e1598eca733957f07b3f310caa5..d0988619dcade410815840c611ec9e0585f80621 100644 (file)
@@ -157,6 +157,30 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
     end
   end
 
+  describe "Question objects" do
+    setup do
+      user = insert(:user)
+      question = build(:question, user: user)
+      question_activity = build(:question_activity, question: question)
+      activity_data = Map.put(question_activity.data, "object", question.data["id"])
+      meta = [object_data: question.data, local: false]
+
+      {:ok, activity, meta} = ActivityPub.persist(activity_data, meta)
+
+      %{activity: activity, meta: meta}
+    end
+
+    test "enqueues the poll end", %{activity: activity, meta: meta} do
+      {:ok, activity, meta} = SideEffects.handle(activity, meta)
+
+      assert_enqueued(
+        worker: Pleroma.Workers.PollWorker,
+        args: %{op: "poll_end", activity_id: activity.id},
+        scheduled_at: NaiveDateTime.from_iso8601!(meta[:object_data]["closed"])
+      )
+    end
+  end
+
   describe "delete users with confirmation pending" do
     setup do
       user = insert(:user, is_confirmed: false)
index 6a2986b5ffd7c303af4f9d1d77615c0322923b8c..8102845d57a8ee61d038016c2f251bdafb3182dc 100644 (file)
@@ -305,7 +305,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
         |> get("/api/pleroma/admin/reports")
 
       assert json_response(conn, :forbidden) ==
-               %{"error" => "User is not an admin."}
+               %{"error" => "User is not a staff member."}
     end
 
     test "returns 403 when requested by anonymous" do
index a5dfd39342e0776954aff9b44233518d7ff55fd6..4a10a5bc488e651ffbe2b1deb683f5fb9e62a02d 100644 (file)
@@ -18,6 +18,7 @@ defmodule Pleroma.Web.CommonAPITest do
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.AdminAPI.AccountView
   alias Pleroma.Web.CommonAPI
+  alias Pleroma.Workers.PollWorker
 
   import Pleroma.Factory
   import Mock
@@ -48,6 +49,12 @@ defmodule Pleroma.Web.CommonAPITest do
 
       assert object.data["type"] == "Question"
       assert object.data["oneOf"] |> length() == 2
+
+      assert_enqueued(
+        worker: PollWorker,
+        args: %{op: "poll_end", activity_id: activity.id},
+        scheduled_at: NaiveDateTime.from_iso8601!(object.data["closed"])
+      )
     end
   end
 
index d478a81ee7dfbb3a4bc2a0e580bc23a9f3bf548a..ed66d370ab3fdc59ee5cf49a1b9798d04e39a20e 100644 (file)
@@ -16,6 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.CommonAPI
+  alias Pleroma.Workers.ScheduledActivityWorker
 
   import Pleroma.Factory
 
@@ -705,11 +706,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
         |> json_response_and_validate_schema(200)
 
       assert {:ok, %{id: activity_id}} =
-               perform_job(Pleroma.Workers.ScheduledActivityWorker, %{
+               perform_job(ScheduledActivityWorker, %{
                  activity_id: scheduled_id
                })
 
-      assert Repo.all(Oban.Job) == []
+      refute_enqueued(worker: ScheduledActivityWorker)
 
       object =
         Activity
index 496a688d1f79c6908bb5d8e1ac4dfb9a210c3814..8070c03c950032c64f2b833fd3d14cb638a47937 100644 (file)
@@ -196,6 +196,27 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
     test_notifications_rendering([notification], user, [expected])
   end
 
+  test "Poll notification" do
+    user = insert(:user)
+    activity = insert(:question_activity, user: user)
+    {:ok, [notification]} = Notification.create_poll_notifications(activity)
+
+    expected = %{
+      id: to_string(notification.id),
+      pleroma: %{is_seen: false, is_muted: false},
+      type: "poll",
+      account:
+        AccountView.render("show.json", %{
+          user: user,
+          for: user
+        }),
+      status: StatusView.render("show.json", %{activity: activity, for: user}),
+      created_at: Utils.to_masto_date(notification.inserted_at)
+    }
+
+    test_notifications_rendering([notification], user, [expected])
+  end
+
   test "Report notification" do
     reporting_user = insert(:user)
     reported_user = insert(:user)
index ee6fdaae8141c37e76c7d37d72b5993f38be4f4e..9deceb1b5f02cf04842b8c1cd5e4abdb651a04d0 100644 (file)
@@ -150,37 +150,127 @@ defmodule Pleroma.Web.NodeInfoTest do
            )
   end
 
-  test "it shows MRF transparency data if enabled", %{conn: conn} do
-    clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
-    clear_config([:mrf, :transparency], true)
+  describe "Quarantined instances" do
+    setup do
+      clear_config([:mrf, :transparency], true)
+      quarantined_instances = [{"example.com", "reason to quarantine"}]
+      clear_config([:instance, :quarantined_instances], quarantined_instances)
+    end
 
-    simple_config = %{"reject" => ["example.com"]}
-    clear_config(:mrf_simple, simple_config)
+    test "shows quarantined instances data if enabled", %{conn: conn} do
+      expected_config = ["example.com"]
 
-    response =
-      conn
-      |> get("/nodeinfo/2.1.json")
-      |> json_response(:ok)
+      response =
+        conn
+        |> get("/nodeinfo/2.1.json")
+        |> json_response(:ok)
+
+      assert response["metadata"]["federation"]["quarantined_instances"] == expected_config
+    end
+
+    test "shows extra information in the quarantined_info field for relevant entries", %{
+      conn: conn
+    } do
+      clear_config([:mrf, :transparency], true)
 
-    assert response["metadata"]["federation"]["mrf_simple"] == simple_config
+      expected_config = %{
+        "quarantined_instances" => %{
+          "example.com" => %{"reason" => "reason to quarantine"}
+        }
+      }
+
+      response =
+        conn
+        |> get("/nodeinfo/2.1.json")
+        |> json_response(:ok)
+
+      assert response["metadata"]["federation"]["quarantined_instances_info"] == expected_config
+    end
   end
 
-  test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do
-    clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
-    clear_config([:mrf, :transparency], true)
-    clear_config([:mrf, :transparency_exclusions], ["other.site"])
+  describe "MRF SimplePolicy" do
+    setup do
+      clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
+      clear_config([:mrf, :transparency], true)
+    end
 
-    simple_config = %{"reject" => ["example.com", "other.site"]}
-    clear_config(:mrf_simple, simple_config)
+    test "shows MRF transparency data if enabled", %{conn: conn} do
+      simple_config = %{"reject" => [{"example.com", ""}]}
+      clear_config(:mrf_simple, simple_config)
 
-    expected_config = %{"reject" => ["example.com"]}
+      expected_config = %{"reject" => ["example.com"]}
 
-    response =
-      conn
-      |> get("/nodeinfo/2.1.json")
-      |> json_response(:ok)
+      response =
+        conn
+        |> get("/nodeinfo/2.1.json")
+        |> json_response(:ok)
+
+      assert response["metadata"]["federation"]["mrf_simple"] == expected_config
+    end
 
-    assert response["metadata"]["federation"]["mrf_simple"] == expected_config
-    assert response["metadata"]["federation"]["exclusions"] == true
+    test "performs exclusions from MRF transparency data if configured", %{conn: conn} do
+      clear_config([:mrf, :transparency_exclusions], [
+        {"other.site", "We don't want them to know"}
+      ])
+
+      simple_config = %{"reject" => [{"example.com", ""}, {"other.site", ""}]}
+      clear_config(:mrf_simple, simple_config)
+
+      expected_config = %{"reject" => ["example.com"]}
+
+      response =
+        conn
+        |> get("/nodeinfo/2.1.json")
+        |> json_response(:ok)
+
+      assert response["metadata"]["federation"]["mrf_simple"] == expected_config
+      assert response["metadata"]["federation"]["exclusions"] == true
+    end
+
+    test "shows extra information in the mrf_simple_info field for relevant entries", %{
+      conn: conn
+    } do
+      simple_config = %{
+        media_removal: [{"no.media", "LEEWWWDD >//<"}],
+        media_nsfw: [],
+        federated_timeline_removal: [{"no.ftl", ""}],
+        report_removal: [],
+        reject: [
+          {"example.instance", "Some reason"},
+          {"uwu.owo", "awoo to much"},
+          {"no.reason", ""}
+        ],
+        followers_only: [],
+        accept: [],
+        avatar_removal: [],
+        banner_removal: [],
+        reject_deletes: [
+          {"peak.me", "I want to peak at what they don't want me to see, eheh"}
+        ]
+      }
+
+      clear_config(:mrf_simple, simple_config)
+
+      clear_config([:mrf, :transparency_exclusions], [
+        {"peak.me", "I don't want them to know"}
+      ])
+
+      expected_config = %{
+        "media_removal" => %{
+          "no.media" => %{"reason" => "LEEWWWDD >//<"}
+        },
+        "reject" => %{
+          "example.instance" => %{"reason" => "Some reason"},
+          "uwu.owo" => %{"reason" => "awoo to much"}
+        }
+      }
+
+      response =
+        conn
+        |> get("/nodeinfo/2.1.json")
+        |> json_response(:ok)
+
+      assert response["metadata"]["federation"]["mrf_simple_info"] == expected_config
+    end
   end
 end
diff --git a/test/pleroma/web/plugs/user_is_staff_plug_test.exs b/test/pleroma/web/plugs/user_is_staff_plug_test.exs
new file mode 100644 (file)
index 0000000..a0c4061
--- /dev/null
@@ -0,0 +1,47 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.UserIsStaffPlugTest do
+  use Pleroma.Web.ConnCase, async: true
+
+  alias Pleroma.Web.Plugs.UserIsStaffPlug
+  import Pleroma.Factory
+
+  test "accepts a user that is an admin" do
+    user = insert(:user, is_admin: true)
+
+    conn = assign(build_conn(), :user, user)
+
+    ret_conn = UserIsStaffPlug.call(conn, %{})
+
+    assert conn == ret_conn
+  end
+
+  test "accepts a user that is a moderator" do
+    user = insert(:user, is_moderator: true)
+
+    conn = assign(build_conn(), :user, user)
+
+    ret_conn = UserIsStaffPlug.call(conn, %{})
+
+    assert conn == ret_conn
+  end
+
+  test "denies a user that isn't a staff member" do
+    user = insert(:user)
+
+    conn =
+      build_conn()
+      |> assign(:user, user)
+      |> UserIsStaffPlug.call(%{})
+
+    assert conn.status == 403
+  end
+
+  test "denies when a user isn't set" do
+    conn = UserIsStaffPlug.call(build_conn(), %{})
+
+    assert conn.status == 403
+  end
+end
index cc17940b5c078dfc3bf6f05a7de8ddfa47d02228..f030483d80b1e57ed587044b6cb45b7a9dd82a69 100644 (file)
@@ -261,11 +261,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       conn =
         conn
         |> assign(:token, nil)
-        |> post(
-          "/api/pleroma/change_email?#{
-            URI.encode_query(%{password: "hi", email: "test@test.com"})
-          }"
-        )
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_email", %{password: "hi", email: "test@test.com"})
 
       assert json_response_and_validate_schema(conn, 403) == %{
                "error" => "Insufficient permissions: write:accounts."
@@ -274,12 +271,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
 
     test "with proper permissions and invalid password", %{conn: conn} do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_email?#{
-            URI.encode_query(%{password: "hi", email: "test@test.com"})
-          }"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_email", %{password: "hi", email: "test@test.com"})
 
       assert json_response_and_validate_schema(conn, 200) == %{"error" => "Invalid password."}
     end
@@ -288,10 +282,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       conn: conn
     } do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_email?#{URI.encode_query(%{password: "test", email: "foobar"})}"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_email", %{password: "test", email: "foobar"})
 
       assert json_response_and_validate_schema(conn, 200) == %{
                "error" => "Email has invalid format."
@@ -301,7 +294,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
     test "with proper permissions, valid password and no email", %{
       conn: conn
     } do
-      conn = post(conn, "/api/pleroma/change_email?#{URI.encode_query(%{password: "test"})}")
+      conn =
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_email", %{password: "test"})
 
       assert %{"error" => "Missing field: email."} = json_response_and_validate_schema(conn, 400)
     end
@@ -310,10 +306,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       conn: conn
     } do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_email?#{URI.encode_query(%{password: "test", email: ""})}"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_email", %{password: "test", email: ""})
 
       assert json_response_and_validate_schema(conn, 200) == %{"error" => "Email can't be blank."}
     end
@@ -324,10 +319,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       user = insert(:user)
 
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_email?#{URI.encode_query(%{password: "test", email: user.email})}"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_email", %{password: "test", email: user.email})
 
       assert json_response_and_validate_schema(conn, 200) == %{
                "error" => "Email has already been taken."
@@ -338,12 +332,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       conn: conn
     } do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_email?#{
-            URI.encode_query(%{password: "test", email: "cofe@foobar.com"})
-          }"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_email", %{password: "test", email: "cofe@foobar.com"})
 
       assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
     end
@@ -356,15 +347,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       conn =
         conn
         |> assign(:token, nil)
-        |> post(
-          "/api/pleroma/change_password?#{
-            URI.encode_query(%{
-              password: "hi",
-              new_password: "newpass",
-              new_password_confirmation: "newpass"
-            })
-          }"
-        )
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_password", %{
+          "password" => "hi",
+          "new_password" => "newpass",
+          "new_password_confirmation" => "newpass"
+        })
 
       assert json_response_and_validate_schema(conn, 403) == %{
                "error" => "Insufficient permissions: write:accounts."
@@ -373,16 +361,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
 
     test "with proper permissions and invalid password", %{conn: conn} do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_password?#{
-            URI.encode_query(%{
-              password: "hi",
-              new_password: "newpass",
-              new_password_confirmation: "newpass"
-            })
-          }"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_password", %{
+          "password" => "hi",
+          "new_password" => "newpass",
+          "new_password_confirmation" => "newpass"
+        })
 
       assert json_response_and_validate_schema(conn, 200) == %{"error" => "Invalid password."}
     end
@@ -392,16 +377,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
            conn: conn
          } do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_password?#{
-            URI.encode_query(%{
-              password: "test",
-              new_password: "newpass",
-              new_password_confirmation: "notnewpass"
-            })
-          }"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_password", %{
+          "password" => "test",
+          "new_password" => "newpass",
+          "new_password_confirmation" => "notnewpass"
+        })
 
       assert json_response_and_validate_schema(conn, 200) == %{
                "error" => "New password does not match confirmation."
@@ -412,12 +394,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       conn: conn
     } do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_password?#{
-            URI.encode_query(%{password: "test", new_password: "", new_password_confirmation: ""})
-          }"
-        )
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post("/api/pleroma/change_password", %{
+          password: "test",
+          new_password: "",
+          new_password_confirmation: ""
+        })
 
       assert json_response_and_validate_schema(conn, 200) == %{
                "error" => "New password can't be blank."
@@ -429,15 +412,15 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       user: user
     } do
       conn =
-        post(
-          conn,
-          "/api/pleroma/change_password?#{
-            URI.encode_query(%{
-              password: "test",
-              new_password: "newpass",
-              new_password_confirmation: "newpass"
-            })
-          }"
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> post(
+          "/api/pleroma/change_password",
+          %{
+            password: "test",
+            new_password: "newpass",
+            new_password_confirmation: "newpass"
+          }
         )
 
       assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
index c267dba4ef41140f404ae2902662197c086b37aa..4a78425ce4d2e3ccb21683a032b330cdda4826ec 100644 (file)
@@ -142,6 +142,11 @@ defmodule Pleroma.Factory do
     }
   end
 
+  def followers_only_note_factory(attrs \\ %{}) do
+    %Pleroma.Object{data: data} = note_factory(attrs)
+    %Pleroma.Object{data: Map.merge(data, %{"to" => [data["actor"] <> "/followers"]})}
+  end
+
   def audio_factory(attrs \\ %{}) do
     text = sequence(:text, &"lain radio episode #{&1}")
 
@@ -208,6 +213,38 @@ defmodule Pleroma.Factory do
     }
   end
 
+  def question_factory(attrs \\ %{}) do
+    user = attrs[:user] || insert(:user)
+
+    data = %{
+      "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
+      "type" => "Question",
+      "actor" => user.ap_id,
+      "attributedTo" => user.ap_id,
+      "attachment" => [],
+      "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+      "cc" => [user.follower_address],
+      "context" => Pleroma.Web.ActivityPub.Utils.generate_context_id(),
+      "closed" => DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601(),
+      "oneOf" => [
+        %{
+          "type" => "Note",
+          "name" => "chocolate",
+          "replies" => %{"totalItems" => 0, "type" => "Collection"}
+        },
+        %{
+          "type" => "Note",
+          "name" => "vanilla",
+          "replies" => %{"totalItems" => 0, "type" => "Collection"}
+        }
+      ]
+    }
+
+    %Pleroma.Object{
+      data: merge_attributes(data, Map.get(attrs, :data, %{}))
+    }
+  end
+
   def direct_note_activity_factory do
     dm = insert(:direct_note)
 
@@ -267,6 +304,33 @@ defmodule Pleroma.Factory do
     |> Map.merge(attrs)
   end
 
+  def followers_only_note_activity_factory(attrs \\ %{}) do
+    user = attrs[:user] || insert(:user)
+    note = insert(:followers_only_note, user: user)
+
+    data_attrs = attrs[:data_attrs] || %{}
+    attrs = Map.drop(attrs, [:user, :note, :data_attrs])
+
+    data =
+      %{
+        "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
+        "type" => "Create",
+        "actor" => note.data["actor"],
+        "to" => note.data["to"],
+        "object" => note.data,
+        "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
+        "context" => note.data["context"]
+      }
+      |> Map.merge(data_attrs)
+
+    %Pleroma.Activity{
+      data: data,
+      actor: data["actor"],
+      recipients: data["to"]
+    }
+    |> Map.merge(attrs)
+  end
+
   def note_activity_factory(attrs \\ %{}) do
     user = attrs[:user] || insert(:user)
     note = attrs[:note] || insert(:note, user: user)
@@ -396,6 +460,33 @@ defmodule Pleroma.Factory do
     }
   end
 
+  def question_activity_factory(attrs \\ %{}) do
+    user = attrs[:user] || insert(:user)
+    question = attrs[:question] || insert(:question, user: user)
+
+    data_attrs = attrs[:data_attrs] || %{}
+    attrs = Map.drop(attrs, [:user, :question, :data_attrs])
+
+    data =
+      %{
+        "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
+        "type" => "Create",
+        "actor" => question.data["actor"],
+        "to" => question.data["to"],
+        "object" => question.data["id"],
+        "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
+        "context" => question.data["context"]
+      }
+      |> Map.merge(data_attrs)
+
+    %Pleroma.Activity{
+      data: data,
+      actor: data["actor"],
+      recipients: data["to"]
+    }
+    |> Map.merge(attrs)
+  end
+
   def oauth_app_factory do
     %Pleroma.Web.OAuth.App{
       client_name: sequence(:client_name, &"Some client #{&1}"),