Merge branch 'chore/remove-reference-to-distsn' into 'develop'
authorlain <lain@soykaf.club>
Thu, 25 Jun 2020 10:42:57 +0000 (10:42 +0000)
committerlain <lain@soykaf.club>
Thu, 25 Jun 2020 10:42:57 +0000 (10:42 +0000)
Remove reference to defunct distsn.org

See merge request pleroma/pleroma!2675

66 files changed:
CHANGELOG.md
config/config.exs
config/description.exs
docs/API/admin_api.md
docs/configuration/cheatsheet.md
docs/configuration/howto_theming_your_instance.md
docs/configuration/mrf.md
docs/configuration/storing_remote_media.md
docs/dev.md
docs/index.md [new file with mode: 0644]
docs/introduction.md [deleted file]
lib/mix/tasks/pleroma/config.ex
lib/mix/tasks/pleroma/refresh_counter_cache.ex
lib/pleroma/application.ex
lib/pleroma/application_requirements.ex [new file with mode: 0644]
lib/pleroma/config/config_db.ex
lib/pleroma/config/deprecation_warnings.ex
lib/pleroma/counter_cache.ex
lib/pleroma/emoji/pack.ex
lib/pleroma/following_relationship.ex
lib/pleroma/http/ex_aws.ex [new file with mode: 0644]
lib/pleroma/http/http.ex
lib/pleroma/http/tzdata.ex [new file with mode: 0644]
lib/pleroma/repo.ex
lib/pleroma/stats.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/activity_pub_controller.ex
lib/pleroma/web/activity_pub/builder.ex
lib/pleroma/web/activity_pub/mrf.ex
lib/pleroma/web/activity_pub/mrf/simple_policy.ex
lib/pleroma/web/activity_pub/object_validator.ex
lib/pleroma/web/activity_pub/object_validators/update_validator.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/side_effects.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
lib/pleroma/web/masto_fe_controller.ex
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
lib/pleroma/web/mastodon_api/views/instance_view.ex
lib/pleroma/web/router.ex
mix.exs
mix.lock
priv/repo/migrations/20200323122421_mrf_config_move_from_instance_namespace.exs [new file with mode: 0644]
priv/repo/migrations/20200508092434_update_counter_cache_table.exs [new file with mode: 0644]
priv/repo/optional_migrations/rum_indexing/20190510135645_add_fts_index_to_objects_two.exs
test/application_requirements_test.exs [new file with mode: 0644]
test/config/deprecation_warnings_test.exs [new file with mode: 0644]
test/fixtures/config/temp.secret.exs
test/http/ex_aws_test.exs [new file with mode: 0644]
test/http/tzdata_test.exs [new file with mode: 0644]
test/http_test.exs
test/repo_test.exs
test/stats_test.exs
test/tasks/config_test.exs
test/tasks/refresh_counter_cache_test.exs
test/user_test.exs
test/web/activity_pub/activity_pub_controller_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/mrf/mrf_test.exs
test/web/activity_pub/object_validator_test.exs
test/web/activity_pub/side_effects_test.exs
test/web/activity_pub/transmogrifier/user_update_handling_test.exs [new file with mode: 0644]
test/web/activity_pub/transmogrifier_test.exs
test/web/admin_api/controllers/admin_api_controller_test.exs
test/web/federator_test.exs
test/web/node_info_test.exs
test/workers/cron/purge_expired_activities_worker_test.exs

index ffd43642a63ebfd4e32a0599779f737254b8fba3..71963d206e7a3bac5ef1e0cbd03c0fd487932383 100644 (file)
@@ -11,11 +11,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Using the `only_media` filter on timelines will now exclude reblog media
 - MFR policy to set global expiration for all local Create activities
 - OGP rich media parser merged with TwitterCard
+- Configuration: `:instance, rewrite_policy` moved to `:mrf, policies`, `:instance, :mrf_transparency` moved to `:mrf, :transparency`, `:instance, :mrf_transparency_exclusions` moved to `:mrf, :transparency_exclusions`. Old config namespace is deprecated.
+
 <details>
   <summary>API Changes</summary>
+
 - **Breaking:** Emoji API: changed methods and renamed routes.
 </details>
 
+<details>
+  <summary>Admin API Changes</summary>
+
+- Status visibility stats: now can return stats per instance.
+
+- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
+</details>
+
 ### Removed
 - **Breaking:** removed `with_move` parameter from notifications timeline.
 
@@ -96,6 +107,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 2. Run database migrations (inside Pleroma directory):
   - OTP: `./bin/pleroma_ctl migrate`
   - From Source: `mix ecto.migrate`
+3. Reset status visibility counters (inside Pleroma directory):
+  - OTP: `./bin/pleroma_ctl refresh_counter_cache`
+  - From Source: `mix pleroma.refresh_counter_cache`
 
 
 ## [2.0.2] - 2020-04-08
index e0888fa9a49555f62eb712ba41ba8f189c215340..5aad26e95806411c600b02ae58ed4371c4e6521e 100644 (file)
@@ -210,7 +210,6 @@ config :pleroma, :instance,
     Pleroma.Web.ActivityPub.Publisher
   ],
   allow_relay: true,
-  rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
   public: true,
   quarantined_instances: [],
   managed_config: true,
@@ -221,8 +220,6 @@ config :pleroma, :instance,
     "text/markdown",
     "text/bbcode"
   ],
-  mrf_transparency: true,
-  mrf_transparency_exclusions: [],
   autofollowed_nicknames: [],
   max_pinned_statuses: 1,
   attachment_links: false,
@@ -693,6 +690,15 @@ config :pleroma, :restrict_unauthenticated,
 
 config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
 
+config :pleroma, :mrf,
+  policies: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
+  transparency: true,
+  transparency_exclusions: []
+
+config :tzdata, :http_client, Pleroma.HTTP.Tzdata
+
+config :ex_aws, http_client: Pleroma.HTTP.ExAws
+
 # Import environment specific config. This must remain at the bottom
 # of this file so it overrides the configuration defined above.
 import_config "#{Mix.env()}.exs"
index 5ed7f753eaaa4857cdd788ac2125f667b76cb54e..f54ac2a2a94d4e4b3350a7866b9708dc275ef268 100644 (file)
@@ -689,17 +689,6 @@ config :pleroma, :config_description, [
         type: :boolean,
         description: "Enable Pleroma's Relay, which makes it possible to follow a whole instance"
       },
-      %{
-        key: :rewrite_policy,
-        type: [:module, {:list, :module}],
-        description:
-          "A list of enabled MRF policies. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
-        suggestions:
-          Generator.list_modules_in_dir(
-            "lib/pleroma/web/activity_pub/mrf",
-            "Elixir.Pleroma.Web.ActivityPub.MRF."
-          )
-      },
       %{
         key: :public,
         type: :boolean,
@@ -742,23 +731,6 @@ config :pleroma, :config_description, [
           "text/bbcode"
         ]
       },
-      %{
-        key: :mrf_transparency,
-        label: "MRF transparency",
-        type: :boolean,
-        description:
-          "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
-      },
-      %{
-        key: :mrf_transparency_exclusions,
-        label: "MRF transparency exclusions",
-        type: {:list, :string},
-        description:
-          "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
-        suggestions: [
-          "exclusion.com"
-        ]
-      },
       %{
         key: :extended_nickname_format,
         type: :boolean,
@@ -3389,5 +3361,41 @@ config :pleroma, :config_description, [
         suggestions: [false]
       }
     ]
+  },
+  %{
+    group: :pleroma,
+    key: :mrf,
+    type: :group,
+    description: "General MRF settings",
+    children: [
+      %{
+        key: :policies,
+        type: [:module, {:list, :module}],
+        description:
+          "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
+        suggestions:
+          Generator.list_modules_in_dir(
+            "lib/pleroma/web/activity_pub/mrf",
+            "Elixir.Pleroma.Web.ActivityPub.MRF."
+          )
+      },
+      %{
+        key: :transparency,
+        label: "MRF transparency",
+        type: :boolean,
+        description:
+          "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
+      },
+      %{
+        key: :transparency_exclusions,
+        label: "MRF transparency exclusions",
+        type: {:list, :string},
+        description:
+          "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
+        suggestions: [
+          "exclusion.com"
+        ]
+      }
+    ]
   }
 ]
index b6fb43dcbb9633fca79f9c086880791fd8c823ef..baf895d90923fe7ed33f55e299caec0256f60d99 100644 (file)
@@ -1118,6 +1118,10 @@ Loads json generated from `config/descriptions.exs`.
 
 ### Stats
 
+- Query Params:
+  - *optional* `instance`: **string** instance hostname (without protocol) to get stats for
+- Example: `https://mypleroma.org/api/pleroma/admin/stats?instance=lain.com`
+
 - Response:
 
 ```json
index 7e5f1cd292f195a31df1e9dadcd3a7dfea301d9b..6759d5e937f932e6dc8c252f978ff850fafcc62b 100644 (file)
@@ -36,26 +36,10 @@ To add configuration to your config file, you can copy it from the base config.
 * `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
 * `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
 * `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance.
-* `rewrite_policy`: Message Rewrite Policy, either one or a list. Here are the ones available by default:
-    * `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default).
-    * `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production.
-    * `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certain instances (See [`:mrf_simple`](#mrf_simple)).
-    * `Pleroma.Web.ActivityPub.MRF.TagPolicy`: Applies policies to individual users based on tags, which can be set using pleroma-fe/admin-fe/any other app that supports Pleroma Admin API. For example it allows marking posts from individual users nsfw (sensitive).
-    * `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (See [`:mrf_subchain`](#mrf_subchain)).
-    * `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See [`:mrf_rejectnonpublic`](#mrf_rejectnonpublic)).
-    * `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
-    * `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links.
-    * `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed.
-    * `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)).
-    * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
-    * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
-    * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Adds expiration to all local Create activities (see [`:mrf_activity_expiration`](#mrf_activity_expiration)).
 * `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
 * `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
 * `managed_config`: Whenether the config for pleroma-fe is configured in [:frontend_configurations](#frontend_configurations) or in ``static/config.json``.
 * `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
-* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
-* `mrf_transparency_exclusions`: Exclude specific instance names from MRF transparency.  The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
 * `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.
 * `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
@@ -78,11 +62,30 @@ To add configuration to your config file, you can copy it from the base config.
 * `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
 * `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
 
+## Message rewrite facility
+
+### :mrf
+* `policies`: Message Rewrite Policy, either one or a list. Here are the ones available by default:
+    * `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default).
+    * `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production.
+    * `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See [`:mrf_simple`](#mrf_simple)).
+    * `Pleroma.Web.ActivityPub.MRF.TagPolicy`: Applies policies to individual users based on tags, which can be set using pleroma-fe/admin-fe/any other app that supports Pleroma Admin API. For example it allows marking posts from individual users nsfw (sensitive).
+    * `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (See [`:mrf_subchain`](#mrf_subchain)).
+    * `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See [`:mrf_rejectnonpublic`](#mrf_rejectnonpublic)).
+    * `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
+    * `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links.
+    * `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed.
+    * `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)).
+    * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
+    * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
+* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
+* `transparency_exclusions`: Exclude specific instance names from MRF transparency.  The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
+
 ## Federation
 ### MRF policies
 
 !!! note
-    Configuring MRF policies is not enough for them to take effect. You have to enable them by specifying their module in `rewrite_policy` under [:instance](#instance) section.
+    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.
@@ -969,13 +972,13 @@ config :pleroma, :database_config_whitelist, [
 
 Restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
 
-* `timelines` - public and federated timelines
-  * `local` - public timeline
+* `timelines`: public and federated timelines
+  * `local`: public timeline
   * `federated`
-* `profiles` - user profiles
+* `profiles`: user profiles
   * `local`
   * `remote`
-* `activities` - statuses
+* `activities`: statuses
   * `local`
   * `remote`
 
index d0daf5b25d027b7b7001982dc94c596a95b2103e..cfa00f538a08d3ad2ae20f50b26399f9dd1ebcdf 100644 (file)
@@ -60,7 +60,7 @@ Example of `my-awesome-theme.json` where we add the name "My Awesome Theme"
 
 ### Set as default theme
 
-Now we can set the new theme as default in the [Pleroma FE configuration](General-tips-for-customizing-Pleroma-FE.md).
+Now we can set the new theme as default in the [Pleroma FE configuration](../../../frontend/CONFIGURATION).
 
 Example of adding the new theme in the back-end config files
 ```elixir
index d48d0cc991abd89db3e99079ebed96fd46cd976f..31c66e098a33e14f05455474936424232f252910 100644 (file)
@@ -34,9 +34,9 @@ config :pleroma, :instance,
 To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
 
 ```elixir
-config :pleroma, :instance,
+config :pleroma, :mrf,
   [...]
-  rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy
+  policies: Pleroma.Web.ActivityPub.MRF.SimplePolicy
 ```
 
 Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
@@ -58,8 +58,8 @@ Servers should be configured as lists.
 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`:
 
 ```elixir
-config :pleroma, :instance,
-  rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
+config :pleroma, :mrf,
+  policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
 
 config :pleroma, :mrf_simple,
   media_removal: ["illegalporn.biz"],
@@ -75,7 +75,7 @@ The effects of MRF policies can be very drastic. It is important to use this fun
 
 ## Writing your own MRF Policy
 
-As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `rewrite_policy` config setting.
+As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `policies` config setting.
 
 For example, here is a sample policy module which rewrites all messages to "new message content":
 
@@ -125,8 +125,8 @@ end
 If you save this file as `lib/pleroma/web/activity_pub/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma.  You can enable it in the configuration like so:
 
 ```elixir
-config :pleroma, :instance,
-  rewrite_policy: [
+config :pleroma, :mrf,
+  policies: [
     Pleroma.Web.ActivityPub.MRF.SimplePolicy,
     Pleroma.Web.ActivityPub.MRF.RewritePolicy
   ]
index 7e91fe7d9a199e9c7002af87f336a3860c77a36e..c01985d25d72e70dd1a0f1821eca660f1dbb54ba 100644 (file)
@@ -33,6 +33,6 @@ as soon as the post is received by your instance.
 Add to your `prod.secret.exs`:
 
 ```
-config :pleroma, :instance,
-  rewrite_policy: [Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy]
+config :pleroma, :mrf,
+  policies: [Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy]
 ```
index f1b4cbf8bff8b47bd7a5f7e6bd540fe33aa9558d..9c749c17ce7342df3fd1edcde5ec768b3303f1b3 100644 (file)
@@ -20,4 +20,4 @@ This document contains notes and guidelines for Pleroma developers.
 
 ## Auth-related configuration, OAuth consumer mode etc.
 
-See `Authentication` section of [`docs/configuration/cheatsheet.md`](docs/configuration/cheatsheet.md#authentication).
+See `Authentication` section of [the configuration cheatsheet](configuration/cheatsheet.md#authentication).
diff --git a/docs/index.md b/docs/index.md
new file mode 100644 (file)
index 0000000..1a90d0a
--- /dev/null
@@ -0,0 +1,26 @@
+# Introduction to Pleroma
+## What is Pleroma?
+Pleroma is a federated social networking platform, compatible with Mastodon and other ActivityPub implementations. It is free software licensed under the AGPLv3.
+It actually consists of two components: a backend, named simply Pleroma, and a user-facing frontend, named Pleroma-FE. It also includes the Mastodon frontend, if that's your thing.
+It's part of what we call the fediverse, a federated network of instances which speak common protocols and can communicate with each other.
+One account on an instance is enough to talk to the entire fediverse!
+
+## How can I use it?
+
+Pleroma instances are already widely deployed, a list can be found at <https://the-federation.info/pleroma> and <https://fediverse.network/pleroma>.
+
+If you don't feel like joining an existing instance, but instead prefer to deploy your own instance, that's easy too!
+Installation instructions can be found in the installation section of these docs.
+
+## I got an account, now what?
+Great! Now you can explore the fediverse! Open the login page for your Pleroma instance (e.g. <https://pleroma.soykaf.com>) and login with your username and password. (If you don't have an account yet, click on Register)
+
+### Pleroma-FE
+The default front-end used by Pleroma is Pleroma-FE. You can find more information on what it is and how to use it in the [Introduction to Pleroma-FE](../frontend).
+
+### Mastodon interface
+If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too!
+Just add a "/web" after your instance url (e.g. <https://pleroma.soycaf.com/web>) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC!
+The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
+
+Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma.
diff --git a/docs/introduction.md b/docs/introduction.md
deleted file mode 100644 (file)
index a915c14..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-# Introduction to Pleroma
-## What is Pleroma?
-Pleroma is a federated social networking platform, compatible with GNU social, Mastodon and other OStatus and ActivityPub implementations. It is free software licensed under the AGPLv3.
-It actually consists of two components: a backend, named simply Pleroma, and a user-facing frontend, named Pleroma-FE. It also includes the Mastodon frontend, if that's your thing.
-It's part of what we call the fediverse, a federated network of instances which speak common protocols and can communicate with each other.
-One account on an instance is enough to talk to the entire fediverse!
-
-## How can I use it?
-
-Pleroma instances are already widely deployed, a list can be found at <http://distsn.org/pleroma-instances.html>. Information on all existing fediverse instances can be found at <https://fediverse.network/>.
-
-If you don't feel like joining an existing instance, but instead prefer to deploy your own instance, that's easy too!
-Installation instructions can be found in the installation section of these docs.
-
-## I got an account, now what?
-Great! Now you can explore the fediverse! Open the login page for your Pleroma instance (e.g. <https://pleroma.soykaf.com>) and login with your username and password. (If you don't have an account yet, click on Register)
-
-At this point you will have two columns in front of you.
-
-### Left column
-
-- first block: here you can see your avatar, your nickname and statistics (Statuses, Following, Followers). Clicking your profile pic will open your profile.
-Under that you have a text form which allows you to post new statuses. The number on the bottom of the text form is a character counter, every instance can have a different character limit (the default is 5000).
-If you want to mention someone, type @ + name of the person. A drop-down menu will help you in finding the right person.
-Under the text form there are also several visibility options and there is the option to use rich text.
-Under that the icon on the left is for uploading media files and attach them to your post. There is also an emoji-picker and an option to post a poll.
-To post your status, simply press Submit.
-On the top right you will also see a wrench icon. This opens your personal settings.
-
-- second block: Here you can switch between the different timelines:
-   - Timeline: all the people that you follow
-   - Interactions: here you can switch between different timelines where there was interaction with your account. There is Mentions, Repeats and Favorites, and New follows
-   - Direct Messages: these are the Direct Messages sent to you
-   - Public Timeline: all the statutes from the local instance
-   - The Whole Known Network: all public posts the instance knows about, both local and remote!
-   - About: This isn't a Timeline but shows relevant info about the instance. You can find a list of the moderators and admins, Terms of Service, MRF policies and enabled features.
-- Optional third block: This is the Instance panel that can be activated, but is deactivated by default. It's fully customisable and by default has links to the pleroma-fe and Mastodon-fe.
-- fourth block: This is the Notifications block, here you will get notified whenever somebody mentions you, follows you, repeats or favorites one of your statuses.
-
-### Right column
-This is where the interesting stuff happens!
-Depending on the timeline you will see different statuses, but each status has a standard structure:
-
-- Profile pic, name and link to profile. An optional left-arrow if it's a reply to another status (hovering will reveal the reply-to status). Clicking on the profile pic will uncollapse the user's profile.
-- A `+` button on the right allows you to Expand/Collapse an entire discussion thread. It also updates in realtime!
-- An arrow icon allows you to open the status on the instance where it's originating from.
-- The text of the status, including mentions and attachements. If you click on a mention, it will automatically open the profile page of that person.
-- Three buttons (left to right): Reply, Repeat, Favorite. There is also a forth button, this is a dropdown menu for simple moderation like muting the conversation or, if you have moderation rights, delete the status from the server.
-
-### Top right
-
-- The magnifier icon opens the search screen where you can search for statuses, people and hashtags. It's also possible to import statusses from remote servers by pasting the url to the post in the search field.
-- The gear icon gives you general settings
-- If you have admin rights, you'll see an icon that opens the admin interface
-- The last icon is to log out
-
-### Bottom right
-On the bottom right you have a chatbox. Here you can communicate with people on the same instance in realtime. It is local-only, for now, but there are plans to make it extendable to the entire fediverse!
-
-### Mastodon interface
-If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too!
-Just add a "/web" after your instance url (e.g. <https://pleroma.soycaf.com/web>) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC!
-The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
-
-Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma.
index f1b3a8766f12b720387537f2617acb5eeb073d04..d5129d410b8c9cde9cc5b38208344b4e2a334c0a 100644 (file)
@@ -52,6 +52,7 @@ defmodule Mix.Tasks.Pleroma.Config do
 
   defp do_migrate_to_db(config_file) do
     if File.exists?(config_file) do
+      shell_info("Migrating settings from file: #{Path.expand(config_file)}")
       Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
       Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
 
index 15b4dbfa681c9e86bbde1d494121ebff0dc2d3bf..efcbaa3b1e9fc9962ae2a2a770059db4358ee4bd 100644 (file)
@@ -17,30 +17,53 @@ defmodule Mix.Tasks.Pleroma.RefreshCounterCache do
   def run([]) do
     Mix.Pleroma.start_pleroma()
 
-    ["public", "unlisted", "private", "direct"]
-    |> Enum.each(fn visibility ->
-      count = status_visibility_count_query(visibility)
-      name = "status_visibility_#{visibility}"
-      CounterCache.set(name, count)
-      Mix.Pleroma.shell_info("Set #{name} to #{count}")
+    instances =
+      Activity
+      |> distinct([a], true)
+      |> select([a], fragment("split_part(?, '/', 3)", a.actor))
+      |> Repo.all()
+
+    instances
+    |> Enum.with_index(1)
+    |> Enum.each(fn {instance, i} ->
+      counters = instance_counters(instance)
+      CounterCache.set(instance, counters)
+
+      Mix.Pleroma.shell_info(
+        "[#{i}/#{length(instances)}] Setting #{instance} counters: #{inspect(counters)}"
+      )
     end)
 
     Mix.Pleroma.shell_info("Done")
   end
 
-  defp status_visibility_count_query(visibility) do
+  defp instance_counters(instance) do
+    counters = %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
+
     Activity
-    |> where(
+    |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
+    |> where([a], fragment("split_part(?, '/', 3) = ?", a.actor, ^instance))
+    |> select(
+      [a],
+      {fragment(
+         "activity_visibility(?, ?, ?)",
+         a.actor,
+         a.recipients,
+         a.data
+       ), count(a.id)}
+    )
+    |> group_by(
       [a],
       fragment(
-        "activity_visibility(?, ?, ?) = ?",
+        "activity_visibility(?, ?, ?)",
         a.actor,
         a.recipients,
-        a.data,
-        ^visibility
+        a.data
       )
     )
-    |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
-    |> Repo.aggregate(:count, :id, timeout: :timer.minutes(30))
+    |> Repo.all(timeout: :timer.minutes(30))
+    |> Enum.reduce(counters, fn {visibility, count}, acc ->
+      Map.put(acc, visibility, count)
+    end)
   end
 end
index 4a21bf138e51faf98f993d9e5adee949db212325..9615af1229a9541bac56506a19ba63f5e59bd23a 100644 (file)
@@ -39,7 +39,7 @@ defmodule Pleroma.Application do
     Pleroma.HTML.compile_scrubbers()
     Config.DeprecationWarnings.warn()
     Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()
-    Pleroma.Repo.check_migrations_applied!()
+    Pleroma.ApplicationRequirements.verify!()
     setup_instrumenters()
     load_custom_modules()
 
diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex
new file mode 100644 (file)
index 0000000..88575a4
--- /dev/null
@@ -0,0 +1,107 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.ApplicationRequirements do
+  @moduledoc """
+  The module represents the collection of validations to runs before start server.
+  """
+
+  defmodule VerifyError, do: defexception([:message])
+
+  import Ecto.Query
+
+  require Logger
+
+  @spec verify!() :: :ok | VerifyError.t()
+  def verify! do
+    :ok
+    |> check_migrations_applied!()
+    |> check_rum!()
+    |> handle_result()
+  end
+
+  defp handle_result(:ok), do: :ok
+  defp handle_result({:error, message}), do: raise(VerifyError, message: message)
+
+  # Checks for pending migrations.
+  #
+  def check_migrations_applied!(:ok) do
+    unless Pleroma.Config.get(
+             [:i_am_aware_this_may_cause_data_loss, :disable_migration_check],
+             false
+           ) do
+      {_, res, _} =
+        Ecto.Migrator.with_repo(Pleroma.Repo, fn repo ->
+          down_migrations =
+            Ecto.Migrator.migrations(repo)
+            |> Enum.reject(fn
+              {:up, _, _} -> true
+              {:down, _, _} -> false
+            end)
+
+          if length(down_migrations) > 0 do
+            down_migrations_text =
+              Enum.map(down_migrations, fn {:down, id, name} -> "- #{name} (#{id})\n" end)
+
+            Logger.error(
+              "The following migrations were not applied:\n#{down_migrations_text}If you want to start Pleroma anyway, set\nconfig :pleroma, :i_am_aware_this_may_cause_data_loss, disable_migration_check: true"
+            )
+
+            {:error, "Unapplied Migrations detected"}
+          else
+            :ok
+          end
+        end)
+
+      res
+    else
+      :ok
+    end
+  end
+
+  def check_migrations_applied!(result), do: result
+
+  # Checks for settings of RUM indexes.
+  #
+  defp check_rum!(:ok) do
+    {_, res, _} =
+      Ecto.Migrator.with_repo(Pleroma.Repo, fn repo ->
+        migrate =
+          from(o in "columns",
+            where: o.table_name == "objects",
+            where: o.column_name == "fts_content"
+          )
+          |> repo.exists?(prefix: "information_schema")
+
+        setting = Pleroma.Config.get([:database, :rum_enabled], false)
+
+        do_check_rum!(setting, migrate)
+      end)
+
+    res
+  end
+
+  defp check_rum!(result), do: result
+
+  defp do_check_rum!(setting, migrate) do
+    case {setting, migrate} do
+      {true, false} ->
+        Logger.error(
+          "Use `RUM` index is enabled, but were not applied migrations for it.\nIf you want to start Pleroma anyway, set\nconfig :pleroma, :database, rum_enabled: false\nOtherwise apply the following migrations:\n`mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/`"
+        )
+
+        {:error, "Unapplied RUM Migrations detected"}
+
+      {false, true} ->
+        Logger.error(
+          "Detected applied migrations to use `RUM` index, but `RUM` isn't enable in settings.\nIf you want to use `RUM`, set\nconfig :pleroma, :database, rum_enabled: true\nOtherwise roll `RUM` migrations back.\n`mix ecto.rollback --migrations-path priv/repo/optional_migrations/rum_indexing/`"
+        )
+
+        {:error, "RUM Migrations detected"}
+
+      _ ->
+        :ok
+    end
+  end
+end
index 2f4eb8581dba09e75552acea45388f230f7b9108..1a89d8895bda0c40db8117538ddae08d4a76995d 100644 (file)
@@ -167,7 +167,9 @@ defmodule Pleroma.ConfigDB do
     end)
   end
 
-  @spec delete(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()}
+  @spec delete(ConfigDB.t() | map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()}
+  def delete(%ConfigDB{} = config), do: Repo.delete(config)
+
   def delete(params) do
     search_opts = Map.delete(params, :subkeys)
 
index b68ded01f6bd486f575485515cd588c2e0b312e5..0a6c724fbd5090b8f36f6aabd4152869b6ca23e0 100644 (file)
@@ -3,9 +3,23 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Config.DeprecationWarnings do
+  alias Pleroma.Config
+
   require Logger
   alias Pleroma.Config
 
+  @type config_namespace() :: [atom()]
+  @type config_map() :: {config_namespace(), config_namespace(), String.t()}
+
+  @mrf_config_map [
+    {[:instance, :rewrite_policy], [:mrf, :policies],
+     "\n* `config :pleroma, :instance, rewrite_policy` is now `config :pleroma, :mrf, policies`"},
+    {[:instance, :mrf_transparency], [:mrf, :transparency],
+     "\n* `config :pleroma, :instance, mrf_transparency` is now `config :pleroma, :mrf, transparency`"},
+    {[:instance, :mrf_transparency_exclusions], [:mrf, :transparency_exclusions],
+     "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
+  ]
+
   def check_hellthread_threshold do
     if Config.get([:mrf_hellthread, :threshold]) do
       Logger.warn("""
@@ -39,5 +53,35 @@ defmodule Pleroma.Config.DeprecationWarnings do
   def warn do
     check_hellthread_threshold()
     mrf_user_allowlist()
+    check_old_mrf_config()
+  end
+
+  def check_old_mrf_config do
+    warning_preface = """
+    !!!DEPRECATION WARNING!!!
+    Your config is using old namespaces for MRF configuration. They should work for now, but you are advised to change to new namespaces to prevent possible issues later:
+    """
+
+    move_namespace_and_warn(@mrf_config_map, warning_preface)
+  end
+
+  @spec move_namespace_and_warn([config_map()], String.t()) :: :ok
+  def move_namespace_and_warn(config_map, warning_preface) do
+    warning =
+      Enum.reduce(config_map, "", fn
+        {old, new, err_msg}, acc ->
+          old_config = Config.get(old)
+
+          if old_config do
+            Config.put(new, old_config)
+            acc <> err_msg
+          else
+            acc
+          end
+      end)
+
+    if warning != "" do
+      Logger.warn(warning_preface <> warning)
+    end
   end
 end
index 4d348a4134915a783d08547ed8fffae233721d5c..ebd1f603df4c5f7d650a1f5576162f791fb1535a 100644 (file)
@@ -10,32 +10,70 @@ defmodule Pleroma.CounterCache do
   import Ecto.Query
 
   schema "counter_cache" do
-    field(:name, :string)
-    field(:count, :integer)
+    field(:instance, :string)
+    field(:public, :integer)
+    field(:unlisted, :integer)
+    field(:private, :integer)
+    field(:direct, :integer)
   end
 
   def changeset(struct, params) do
     struct
-    |> cast(params, [:name, :count])
-    |> validate_required([:name])
-    |> unique_constraint(:name)
+    |> cast(params, [:instance, :public, :unlisted, :private, :direct])
+    |> validate_required([:instance])
+    |> unique_constraint(:instance)
   end
 
-  def get_as_map(names) when is_list(names) do
+  def get_by_instance(instance) do
     CounterCache
-    |> where([cc], cc.name in ^names)
-    |> Repo.all()
-    |> Enum.group_by(& &1.name, & &1.count)
-    |> Map.new(fn {k, v} -> {k, hd(v)} end)
+    |> select([c], %{
+      "public" => c.public,
+      "unlisted" => c.unlisted,
+      "private" => c.private,
+      "direct" => c.direct
+    })
+    |> where([c], c.instance == ^instance)
+    |> Repo.one()
+    |> case do
+      nil -> %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
+      val -> val
+    end
   end
 
-  def set(name, count) do
+  def get_sum do
+    CounterCache
+    |> select([c], %{
+      "public" => type(sum(c.public), :integer),
+      "unlisted" => type(sum(c.unlisted), :integer),
+      "private" => type(sum(c.private), :integer),
+      "direct" => type(sum(c.direct), :integer)
+    })
+    |> Repo.one()
+  end
+
+  def set(instance, values) do
+    params =
+      Enum.reduce(
+        ["public", "private", "unlisted", "direct"],
+        %{"instance" => instance},
+        fn param, acc ->
+          Map.put_new(acc, param, Map.get(values, param, 0))
+        end
+      )
+
     %CounterCache{}
-    |> changeset(%{"name" => name, "count" => count})
+    |> changeset(params)
     |> Repo.insert(
-      on_conflict: [set: [count: count]],
+      on_conflict: [
+        set: [
+          public: params["public"],
+          private: params["private"],
+          unlisted: params["unlisted"],
+          direct: params["direct"]
+        ]
+      ],
       returning: true,
-      conflict_target: :name
+      conflict_target: :instance
     )
   end
 end
index 787ff8141772f3738cb7c041ce789a1ff6d82ad1..d076ae3125c5318815dba7d59042862c9f1399e8 100644 (file)
@@ -45,6 +45,7 @@ defmodule Pleroma.Emoji.Pack do
       shortcodes =
         pack.files
         |> Map.keys()
+        |> Enum.sort()
         |> paginate(opts[:page], opts[:page_size])
 
       pack = Map.put(pack, :files, Map.take(pack.files, shortcodes))
index 093b1f4050d62a94f70c865bc27262f2cb70b6ca..c2020d30a876a9169682f8271861d9e808165bf2 100644 (file)
@@ -124,6 +124,7 @@ defmodule Pleroma.FollowingRelationship do
     |> join(:inner, [r], f in assoc(r, :follower))
     |> where([r], r.state == ^:follow_pending)
     |> where([r], r.following_id == ^id)
+    |> where([r, f], f.deactivated != true)
     |> select([r, f], f)
     |> Repo.all()
   end
diff --git a/lib/pleroma/http/ex_aws.ex b/lib/pleroma/http/ex_aws.ex
new file mode 100644 (file)
index 0000000..e53e640
--- /dev/null
@@ -0,0 +1,22 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.HTTP.ExAws do
+  @moduledoc false
+
+  @behaviour ExAws.Request.HttpClient
+
+  alias Pleroma.HTTP
+
+  @impl true
+  def request(method, url, body \\ "", headers \\ [], http_opts \\ []) do
+    case HTTP.request(method, url, body, headers, http_opts) do
+      {:ok, env} ->
+        {:ok, %{status_code: env.status, headers: env.headers, body: env.body}}
+
+      {:error, reason} ->
+        {:error, %{reason: reason}}
+    end
+  end
+end
index 583b564842fe79477f0205ef758ab2b5c094351f..66ca7536766918a3ffa6734fe301aa3857cfcb6e 100644 (file)
@@ -16,6 +16,7 @@ defmodule Pleroma.HTTP do
   require Logger
 
   @type t :: __MODULE__
+  @type method() :: :get | :post | :put | :delete | :head
 
   @doc """
   Performs GET request.
@@ -28,6 +29,9 @@ defmodule Pleroma.HTTP do
   def get(nil, _, _), do: nil
   def get(url, headers, options), do: request(:get, url, "", headers, options)
 
+  @spec head(Request.url(), Request.headers(), keyword()) :: {:ok, Env.t()} | {:error, any()}
+  def head(url, headers \\ [], options \\ []), do: request(:head, url, "", headers, options)
+
   @doc """
   Performs POST request.
 
@@ -42,7 +46,7 @@ defmodule Pleroma.HTTP do
   Builds and performs http request.
 
   # Arguments:
-  `method` - :get, :post, :put, :delete
+  `method` - :get, :post, :put, :delete, :head
   `url` - full url
   `body` - request body
   `headers` - a keyworld list of headers, e.g. `[{"content-type", "text/plain"}]`
@@ -52,7 +56,7 @@ defmodule Pleroma.HTTP do
   `{:ok, %Tesla.Env{}}` or `{:error, error}`
 
   """
-  @spec request(atom(), Request.url(), String.t(), Request.headers(), keyword()) ::
+  @spec request(method(), Request.url(), String.t(), Request.headers(), keyword()) ::
           {:ok, Env.t()} | {:error, any()}
   def request(method, url, body, headers, options) when is_binary(url) do
     uri = URI.parse(url)
diff --git a/lib/pleroma/http/tzdata.ex b/lib/pleroma/http/tzdata.ex
new file mode 100644 (file)
index 0000000..34bb253
--- /dev/null
@@ -0,0 +1,25 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.HTTP.Tzdata do
+  @moduledoc false
+
+  @behaviour Tzdata.HTTPClient
+
+  alias Pleroma.HTTP
+
+  @impl true
+  def get(url, headers, options) do
+    with {:ok, %Tesla.Env{} = env} <- HTTP.get(url, headers, options) do
+      {:ok, {env.status, env.headers, env.body}}
+    end
+  end
+
+  @impl true
+  def head(url, headers, options) do
+    with {:ok, %Tesla.Env{} = env} <- HTTP.head(url, headers, options) do
+      {:ok, {env.status, env.headers}}
+    end
+  end
+end
index 6d85d70bc66c9ac6de1d0266c4937572402c2e95..f317e4d582e6cea6fee974190e30015797cffb8a 100644 (file)
@@ -11,9 +11,7 @@ defmodule Pleroma.Repo do
   import Ecto.Query
   require Logger
 
-  defmodule Instrumenter do
-    use Prometheus.EctoInstrumenter
-  end
+  defmodule Instrumenter, do: use(Prometheus.EctoInstrumenter)
 
   @doc """
   Dynamically loads the repository url from the
@@ -51,35 +49,6 @@ defmodule Pleroma.Repo do
     end
   end
 
-  def check_migrations_applied!() do
-    unless Pleroma.Config.get(
-             [:i_am_aware_this_may_cause_data_loss, :disable_migration_check],
-             false
-           ) do
-      Ecto.Migrator.with_repo(__MODULE__, fn repo ->
-        down_migrations =
-          Ecto.Migrator.migrations(repo)
-          |> Enum.reject(fn
-            {:up, _, _} -> true
-            {:down, _, _} -> false
-          end)
-
-        if length(down_migrations) > 0 do
-          down_migrations_text =
-            Enum.map(down_migrations, fn {:down, id, name} -> "- #{name} (#{id})\n" end)
-
-          Logger.error(
-            "The following migrations were not applied:\n#{down_migrations_text}If you want to start Pleroma anyway, set\nconfig :pleroma, :i_am_aware_this_may_cause_data_loss, disable_migration_check: true"
-          )
-
-          raise Pleroma.Repo.UnappliedMigrationsError
-        end
-      end)
-    else
-      :ok
-    end
-  end
-
   def chunk_stream(query, chunk_size) do
     # We don't actually need start and end funcitons of resource streaming,
     # but it seems to be the only way to not fetch records one-by-one and
@@ -107,7 +76,3 @@ defmodule Pleroma.Repo do
     )
   end
 end
-
-defmodule Pleroma.Repo.UnappliedMigrationsError do
-  defexception message: "Unapplied Migrations detected"
-end
index 6b3a8a41f738801e8ffc0817d066cd3212d0ef74..9a03f01db7087d1766599908748a062050b14354 100644 (file)
@@ -97,20 +97,11 @@ defmodule Pleroma.Stats do
     }
   end
 
-  def get_status_visibility_count do
-    counter_cache =
-      CounterCache.get_as_map([
-        "status_visibility_public",
-        "status_visibility_private",
-        "status_visibility_unlisted",
-        "status_visibility_direct"
-      ])
-
-    %{
-      public: counter_cache["status_visibility_public"] || 0,
-      unlisted: counter_cache["status_visibility_unlisted"] || 0,
-      private: counter_cache["status_visibility_private"] || 0,
-      direct: counter_cache["status_visibility_direct"] || 0
-    }
+  def get_status_visibility_count(instance \\ nil) do
+    if is_nil(instance) do
+      CounterCache.get_sum()
+    else
+      CounterCache.get_by_instance(instance)
+    end
   end
 end
index 3e4d0a2bedd5bd30cdac8bf845b378e456607997..7cd3eab3906c76579c69fe73ca34d0f0e2f03a1c 100644 (file)
@@ -321,28 +321,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  @spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
-  def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
-    local = !(params[:local] == false)
-    activity_id = params[:activity_id]
-
-    data =
-      %{
-        "to" => to,
-        "cc" => cc,
-        "type" => "Update",
-        "actor" => actor,
-        "object" => object
-      }
-      |> Maps.put_if_present("id", activity_id)
-
-    with {:ok, activity} <- insert(data, local),
-         _ <- notify_and_stream(activity),
-         :ok <- maybe_federate(activity) do
-      {:ok, activity}
-    end
-  end
-
   @spec follow(User.t(), User.t(), String.t() | nil, boolean(), keyword()) ::
           {:ok, Activity.t()} | {:error, any()}
   def follow(follower, followed, activity_id \\ nil, local \\ true, opts \\ []) do
index f0b5c6e935eca254b6238f3d8fb44be2bd303ac2..220c4fe52cce85789b2f762acbebf5200484ed81 100644 (file)
@@ -514,7 +514,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
     {new_user, for_user}
   end
 
-  # TODO: Add support for "object" field
   @doc """
   Endpoint based on <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>
 
@@ -525,6 +524,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   Response:
   - HTTP Code: 201 Created
   - HTTP Body: ActivityPub object to be inserted into another's `attachment` field
+
+  Note: Will not point to a URL with a `Location` header because no standalone Activity has been created.
   """
   def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
     with {:ok, object} <-
index 1aac62c69e446d3162020c20c50e6d237210dbe9..135a5c431eca0c271cda0e2479170419a8583271 100644 (file)
@@ -123,6 +123,21 @@ defmodule Pleroma.Web.ActivityPub.Builder do
     end
   end
 
+  # Retricted to user updates for now, always public
+  @spec update(User.t(), Object.t()) :: {:ok, map(), keyword()}
+  def update(actor, object) do
+    to = [Pleroma.Constants.as_public(), actor.follower_address]
+
+    {:ok,
+     %{
+       "id" => Utils.generate_activity_id(),
+       "type" => "Update",
+       "actor" => actor.ap_id,
+       "object" => object,
+       "to" => to
+     }, []}
+  end
+
   @spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
   def announce(actor, object, options \\ []) do
     public? = Keyword.get(options, :public, false)
index 5a4a76085dd41e95016120e4c82fc21f607fc5a7..206d6af52baf91545916d3fbc4e7c546639f995e 100644 (file)
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
   def filter(%{} = object), do: get_policies() |> filter(object)
 
   def get_policies do
-    Pleroma.Config.get([:instance, :rewrite_policy], []) |> get_policies()
+    Pleroma.Config.get([:mrf, :policies], []) |> get_policies()
   end
 
   defp get_policies(policy) when is_atom(policy), do: [policy]
@@ -51,7 +51,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
       get_policies()
       |> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)
 
-    exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions])
+    exclusions = Pleroma.Config.get([:mrf, :transparency_exclusions])
 
     base =
       %{
index b7dcb1b861026622590103e1451f029fe75ecaf7..9cea6bcf9209868e7f6bf403a82d8c10c040f193 100644 (file)
@@ -3,21 +3,23 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.MRF
   @moduledoc "Filter activities depending on their origin instance"
   @behaviour Pleroma.Web.ActivityPub.MRF
 
+  alias Pleroma.Config
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.MRF
+
   require Pleroma.Constants
 
   defp check_accept(%{host: actor_host} = _actor_info, object) do
     accepts =
-      Pleroma.Config.get([:mrf_simple, :accept])
+      Config.get([:mrf_simple, :accept])
       |> MRF.subdomains_regex()
 
     cond do
       accepts == [] -> {:ok, object}
-      actor_host == Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
+      actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
       MRF.subdomain_match?(accepts, actor_host) -> {:ok, object}
       true -> {:reject, nil}
     end
@@ -25,7 +27,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_reject(%{host: actor_host} = _actor_info, object) do
     rejects =
-      Pleroma.Config.get([:mrf_simple, :reject])
+      Config.get([:mrf_simple, :reject])
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(rejects, actor_host) do
@@ -41,7 +43,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
        )
        when length(child_attachment) > 0 do
     media_removal =
-      Pleroma.Config.get([:mrf_simple, :media_removal])
+      Config.get([:mrf_simple, :media_removal])
       |> MRF.subdomains_regex()
 
     object =
@@ -65,7 +67,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
          } = object
        ) do
     media_nsfw =
-      Pleroma.Config.get([:mrf_simple, :media_nsfw])
+      Config.get([:mrf_simple, :media_nsfw])
       |> MRF.subdomains_regex()
 
     object =
@@ -85,7 +87,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
     timeline_removal =
-      Pleroma.Config.get([:mrf_simple, :federated_timeline_removal])
+      Config.get([:mrf_simple, :federated_timeline_removal])
       |> MRF.subdomains_regex()
 
     object =
@@ -108,7 +110,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
     report_removal =
-      Pleroma.Config.get([:mrf_simple, :report_removal])
+      Config.get([:mrf_simple, :report_removal])
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(report_removal, actor_host) do
@@ -122,7 +124,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
     avatar_removal =
-      Pleroma.Config.get([:mrf_simple, :avatar_removal])
+      Config.get([:mrf_simple, :avatar_removal])
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(avatar_removal, actor_host) do
@@ -136,7 +138,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
     banner_removal =
-      Pleroma.Config.get([:mrf_simple, :banner_removal])
+      Config.get([:mrf_simple, :banner_removal])
       |> MRF.subdomains_regex()
 
     if MRF.subdomain_match?(banner_removal, actor_host) do
@@ -197,10 +199,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   @impl true
   def describe do
-    exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions])
+    exclusions = Config.get([:mrf, :transparency_exclusions])
 
     mrf_simple =
-      Pleroma.Config.get(:mrf_simple)
+      Config.get(:mrf_simple)
       |> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end)
       |> Enum.into(%{})
 
index 6a83a2c33706a8fee3ad3a81433aec7811722297..2c657b46788b79f2b45ed0033d2b0767945fe9ae 100644 (file)
@@ -19,10 +19,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
   alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
   alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
   alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
+  alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
 
   @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
   def validate(object, meta)
 
+  def validate(%{"type" => "Update"} = update_activity, meta) do
+    with {:ok, update_activity} <-
+           update_activity
+           |> UpdateValidator.cast_and_validate()
+           |> Ecto.Changeset.apply_action(:insert) do
+      update_activity = stringify_keys(update_activity)
+      {:ok, update_activity, meta}
+    end
+  end
+
   def validate(%{"type" => "Undo"} = object, meta) do
     with {:ok, object} <-
            object
diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex
new file mode 100644 (file)
index 0000000..b4ba5ed
--- /dev/null
@@ -0,0 +1,59 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
+  use Ecto.Schema
+
+  alias Pleroma.EctoType.ActivityPub.ObjectValidators
+
+  import Ecto.Changeset
+  import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+
+  @primary_key false
+
+  embedded_schema do
+    field(:id, ObjectValidators.ObjectID, primary_key: true)
+    field(:type, :string)
+    field(:actor, ObjectValidators.ObjectID)
+    field(:to, ObjectValidators.Recipients, default: [])
+    field(:cc, ObjectValidators.Recipients, default: [])
+    # In this case, we save the full object in this activity instead of just a
+    # reference, so we can always see what was actually changed by this.
+    field(:object, :map)
+  end
+
+  def cast_data(data) do
+    %__MODULE__{}
+    |> cast(data, __schema__(:fields))
+  end
+
+  def validate_data(cng) do
+    cng
+    |> validate_required([:id, :type, :actor, :to, :cc, :object])
+    |> validate_inclusion(:type, ["Update"])
+    |> validate_actor_presence()
+    |> validate_updating_rights()
+  end
+
+  def cast_and_validate(data) do
+    data
+    |> cast_data
+    |> validate_data
+  end
+
+  # For now we only support updating users, and here the rule is easy:
+  # object id == actor id
+  def validate_updating_rights(cng) do
+    with actor = get_field(cng, :actor),
+         object = get_field(cng, :object),
+         {:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
+         true <- actor == object_id do
+      cng
+    else
+      _e ->
+        cng
+        |> add_error(:object, "Can't be updated by this actor")
+    end
+  end
+end
index 1a1cc675cc3e5b0909c44e95157702c2666e4041..de143b8f0a6a90b53cb44d815b4989da9ad79834 100644 (file)
@@ -20,6 +20,26 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
 
   def handle(object, meta \\ [])
 
+  # Tasks this handles:
+  # - Update the user
+  #
+  # For a local user, we also get a changeset with the full information, so we
+  # can update non-federating, non-activitypub settings as well.
+  def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do
+    if changeset = Keyword.get(meta, :user_update_changeset) do
+      changeset
+      |> User.update_and_set_cache()
+    else
+      {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object)
+
+      User.get_by_ap_id(updated_object["id"])
+      |> User.remote_user_changeset(new_user_data)
+      |> User.update_and_set_cache()
+    end
+
+    {:ok, object, meta}
+  end
+
   # Tasks this handles:
   # - Add like to object
   # - Set up notification
index 1c60ef8f56047a3c16fc445d808b52612cab64b0..4e318e89cf2d9d4cd7f08b08051b62ba1d74d5a9 100644 (file)
@@ -684,35 +684,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   end
 
   def handle_incoming(
-        %{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
-          data,
+        %{"type" => "Update"} = data,
         _options
-      )
-      when object_type in [
-             "Person",
-             "Application",
-             "Service",
-             "Organization"
-           ] do
-    with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
-      {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
-
-      actor
-      |> User.remote_user_changeset(new_user_data)
-      |> User.update_and_set_cache()
-
-      ActivityPub.update(%{
-        local: false,
-        to: data["to"] || [],
-        cc: data["cc"] || [],
-        object: object,
-        actor: actor_id,
-        activity_id: data["id"]
-      })
-    else
-      e ->
-        Logger.error(e)
-        :error
+      ) do
+    with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
+         {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
+      {:ok, activity}
     end
   end
 
index db2413dfe6015c6b916bc2bb862e2d8e035d2874..f9545d8950caf135aca2ec223ada5113a4ded57b 100644 (file)
@@ -643,10 +643,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     json(conn, "")
   end
 
-  def stats(conn, _) do
-    count = Stats.get_status_visibility_count()
+  def stats(conn, params) do
+    counters = Stats.get_status_visibility_count(params["instance"])
 
-    json(conn, %{"status_visibility" => count})
+    json(conn, %{"status_visibility" => counters})
   end
 
   defp page_params(params) do
index d0d8bc8eb9c5ef3bb28a18b2025937a8201ba43d..43ec700219f7b75852f001444ac4bb6a4bd052b3 100644 (file)
@@ -49,7 +49,7 @@ defmodule Pleroma.Web.MastoFEController do
     |> render("manifest.json")
   end
 
-  @doc "PUT /api/web/settings"
+  @doc "PUT /api/web/settings: Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere"
   def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
     with {:ok, _} <- User.mastodon_settings_update(user, settings) do
       json(conn, %{})
index d50e7c5dd839ea3ef23c7a8c15cc424a338ff81d..7a88a847c412f2818eddb863f54df41ac95980b1 100644 (file)
@@ -20,6 +20,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
   alias Pleroma.Plugs.RateLimiter
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Builder
+  alias Pleroma.Web.ActivityPub.Pipeline
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.MastodonAPI.ListView
   alias Pleroma.Web.MastodonAPI.MastodonAPI
@@ -182,34 +184,39 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
       end)
       |> Maps.put_if_present(:actor_type, params[:actor_type])
 
-    changeset = User.update_changeset(user, user_params)
-
-    with {:ok, user} <- User.update_and_set_cache(changeset) do
-      user
-      |> build_update_activity_params()
-      |> ActivityPub.update()
-
-      render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
+    # What happens here:
+    #
+    # We want to update the user through the pipeline, but the ActivityPub
+    # update information is not quite enough for this, because this also
+    # contains local settings that don't federate and don't even appear
+    # in the Update activity.
+    #
+    # So we first build the normal local changeset, then apply it to the
+    # user data, but don't persist it. With this, we generate the object
+    # data for our update activity. We feed this and the changeset as meta
+    # inforation into the pipeline, where they will be properly updated and
+    # federated.
+    with changeset <- User.update_changeset(user, user_params),
+         {:ok, unpersisted_user} <- Ecto.Changeset.apply_action(changeset, :update),
+         updated_object <-
+           Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
+           |> Map.delete("@context"),
+         {:ok, update_data, []} <- Builder.update(user, updated_object),
+         {:ok, _update, _} <-
+           Pipeline.common_pipeline(update_data,
+             local: true,
+             user_update_changeset: changeset
+           ) do
+      render(conn, "show.json",
+        user: unpersisted_user,
+        for: unpersisted_user,
+        with_pleroma_settings: true
+      )
     else
       _e -> render_error(conn, :forbidden, "Invalid request")
     end
   end
 
-  # Hotfix, handling will be redone with the pipeline
-  defp build_update_activity_params(user) do
-    object =
-      Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
-      |> Map.delete("@context")
-
-    %{
-      local: true,
-      to: [user.follower_address],
-      cc: [],
-      object: object,
-      actor: user.ap_id
-    }
-  end
-
   defp normalize_fields_attributes(fields) do
     if Enum.all?(fields, &is_tuple/1) do
       Enum.map(fields, fn {_, v} -> v end)
index c6b54e5701ea9a0ec0f749e1a654d4f1452f1648..35c2fc25cf06314defc2a101d204aecf2391b27c 100644 (file)
@@ -78,7 +78,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
   def federation do
     quarantined = Config.get([:instance, :quarantined_instances], [])
 
-    if Config.get([:instance, :mrf_transparency]) do
+    if Config.get([:mrf, :transparency]) do
       {:ok, data} = MRF.describe()
 
       data
index eda74a171820688f6cef689143382d8cb4b4cfad..419aa55e4589d76882e5d65832c1e2d958eaf4b8 100644 (file)
@@ -467,6 +467,7 @@ defmodule Pleroma.Web.Router do
   scope "/api/web", Pleroma.Web do
     pipe_through(:authenticated_api)
 
+    # Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
     put("/settings", MastoFEController, :put_settings)
   end
 
diff --git a/mix.exs b/mix.exs
index 4d13e95d7195f3f7548f670d5ca4b0396b2ba946..b638be541870d1ca44a6a822179b7c2556b9f03b 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -117,7 +117,7 @@ defmodule Pleroma.Mixfile do
   defp deps do
     [
       {:phoenix, "~> 1.4.8"},
-      {:tzdata, "~> 0.5.21"},
+      {:tzdata, "~> 1.0.3"},
       {:plug_cowboy, "~> 2.0"},
       {:phoenix_pubsub, "~> 1.1"},
       {:phoenix_ecto, "~> 4.0"},
index 5383c2c6ed5ae3b7d5645a83e8aeb949700b93c9..5ad49391dbab3a0ab009aad04fe9f8482b27a97c 100644 (file)
--- a/mix.lock
+++ b/mix.lock
   "tesla": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/tesla.git", "61b7503cef33f00834f78ddfafe0d5d9dec2270b", [ref: "61b7503cef33f00834f78ddfafe0d5d9dec2270b"]},
   "timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"},
   "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
-  "tzdata": {:hex, :tzdata, "0.5.22", "f2ba9105117ee0360eae2eca389783ef7db36d533899b2e84559404dbc77ebb8", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cd66c8a1e6a9e121d1f538b01bef459334bb4029a1ffb4eeeb5e4eae0337e7b6"},
+  "tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"},
   "ueberauth": {:hex, :ueberauth, "0.6.2", "25a31111249d60bad8b65438b2306a4dc91f3208faa62f5a8c33e8713989b2e8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "db9fbfb5ac707bc4f85a297758406340bf0358b4af737a88113c1a9eee120ac7"},
   "unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
   "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
diff --git a/priv/repo/migrations/20200323122421_mrf_config_move_from_instance_namespace.exs b/priv/repo/migrations/20200323122421_mrf_config_move_from_instance_namespace.exs
new file mode 100644 (file)
index 0000000..ef36c4e
--- /dev/null
@@ -0,0 +1,39 @@
+defmodule Pleroma.Repo.Migrations.MrfConfigMoveFromInstanceNamespace do
+  use Ecto.Migration
+
+  alias Pleroma.ConfigDB
+
+  @old_keys [:rewrite_policy, :mrf_transparency, :mrf_transparency_exclusions]
+  def change do
+    config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+
+    if config do
+      mrf =
+        config.value
+        |> Keyword.take(@old_keys)
+        |> Keyword.new(fn
+          {:rewrite_policy, policies} -> {:policies, policies}
+          {:mrf_transparency, transparency} -> {:transparency, transparency}
+          {:mrf_transparency_exclusions, exclusions} -> {:transparency_exclusions, exclusions}
+        end)
+
+      if mrf != [] do
+        {:ok, _} =
+          %ConfigDB{}
+          |> ConfigDB.changeset(%{group: :pleroma, key: :mrf, value: mrf})
+          |> Pleroma.Repo.insert()
+
+        new_instance = Keyword.drop(config.value, @old_keys)
+
+        if new_instance != [] do
+          {:ok, _} =
+            config
+            |> ConfigDB.changeset(%{value: new_instance})
+            |> Pleroma.Repo.update()
+        else
+          {:ok, _} = ConfigDB.delete(config)
+        end
+      end
+    end
+  end
+end
diff --git a/priv/repo/migrations/20200508092434_update_counter_cache_table.exs b/priv/repo/migrations/20200508092434_update_counter_cache_table.exs
new file mode 100644 (file)
index 0000000..7383448
--- /dev/null
@@ -0,0 +1,143 @@
+defmodule Pleroma.Repo.Migrations.UpdateCounterCacheTable do
+  use Ecto.Migration
+
+  @function_name "update_status_visibility_counter_cache"
+  @trigger_name "status_visibility_counter_cache_trigger"
+
+  def up do
+    execute("drop trigger if exists #{@trigger_name} on activities")
+    execute("drop function if exists #{@function_name}()")
+    drop_if_exists(unique_index(:counter_cache, [:name]))
+    drop_if_exists(table(:counter_cache))
+
+    create_if_not_exists table(:counter_cache) do
+      add(:instance, :string, null: false)
+      add(:direct, :bigint, null: false, default: 0)
+      add(:private, :bigint, null: false, default: 0)
+      add(:unlisted, :bigint, null: false, default: 0)
+      add(:public, :bigint, null: false, default: 0)
+    end
+
+    create_if_not_exists(unique_index(:counter_cache, [:instance]))
+
+    """
+    CREATE OR REPLACE FUNCTION #{@function_name}()
+    RETURNS TRIGGER AS
+    $$
+      DECLARE
+        hostname character varying(255);
+        visibility_new character varying(64);
+        visibility_old character varying(64);
+        actor character varying(255);
+      BEGIN
+      IF TG_OP = 'DELETE' THEN
+        actor := OLD.actor;
+      ELSE
+        actor := NEW.actor;
+      END IF;
+      hostname := split_part(actor, '/', 3);
+      IF TG_OP = 'INSERT' THEN
+        visibility_new := activity_visibility(NEW.actor, NEW.recipients, NEW.data);
+        IF NEW.data->>'type' = 'Create'
+            AND visibility_new IN ('public', 'unlisted', 'private', 'direct') THEN
+          EXECUTE format('INSERT INTO "counter_cache" ("instance", %1$I) VALUES ($1, 1)
+                          ON CONFLICT ("instance") DO
+                          UPDATE SET %1$I = "counter_cache".%1$I + 1', visibility_new)
+                          USING hostname;
+        END IF;
+        RETURN NEW;
+      ELSIF TG_OP = 'UPDATE' THEN
+        visibility_new := activity_visibility(NEW.actor, NEW.recipients, NEW.data);
+        visibility_old := activity_visibility(OLD.actor, OLD.recipients, OLD.data);
+        IF (NEW.data->>'type' = 'Create')
+            AND (OLD.data->>'type' = 'Create')
+            AND visibility_new != visibility_old
+            AND visibility_new IN ('public', 'unlisted', 'private', 'direct') THEN
+          EXECUTE format('UPDATE "counter_cache" SET
+                          %1$I = greatest("counter_cache".%1$I - 1, 0),
+                          %2$I = "counter_cache".%2$I + 1
+                          WHERE "instance" = $1', visibility_old, visibility_new)
+                          USING hostname;
+        END IF;
+        RETURN NEW;
+      ELSIF TG_OP = 'DELETE' THEN
+        IF OLD.data->>'type' = 'Create' THEN
+          visibility_old := activity_visibility(OLD.actor, OLD.recipients, OLD.data);
+          EXECUTE format('UPDATE "counter_cache" SET
+                          %1$I = greatest("counter_cache".%1$I - 1, 0)
+                          WHERE "instance" = $1', visibility_old)
+                          USING hostname;
+        END IF;
+        RETURN OLD;
+      END IF;
+      END;
+    $$
+    LANGUAGE 'plpgsql';
+    """
+    |> execute()
+
+    execute("DROP TRIGGER IF EXISTS #{@trigger_name} ON activities")
+
+    """
+    CREATE TRIGGER #{@trigger_name}
+    BEFORE
+      INSERT
+      OR UPDATE of recipients, data
+      OR DELETE
+    ON activities
+    FOR EACH ROW
+      EXECUTE PROCEDURE #{@function_name}();
+    """
+    |> execute()
+  end
+
+  def down do
+    execute("DROP TRIGGER IF EXISTS #{@trigger_name} ON activities")
+    execute("DROP FUNCTION IF EXISTS #{@function_name}()")
+    drop_if_exists(unique_index(:counter_cache, [:instance]))
+    drop_if_exists(table(:counter_cache))
+
+    create_if_not_exists table(:counter_cache) do
+      add(:name, :string, null: false)
+      add(:count, :bigint, null: false, default: 0)
+    end
+
+    create_if_not_exists(unique_index(:counter_cache, [:name]))
+
+    """
+    CREATE OR REPLACE FUNCTION #{@function_name}()
+    RETURNS TRIGGER AS
+    $$
+      DECLARE
+      BEGIN
+      IF TG_OP = 'INSERT' THEN
+          IF NEW.data->>'type' = 'Create' THEN
+            EXECUTE 'INSERT INTO counter_cache (name, count) VALUES (''status_visibility_' || activity_visibility(NEW.actor, NEW.recipients, NEW.data) || ''', 1) ON CONFLICT (name) DO UPDATE SET count = counter_cache.count + 1';
+          END IF;
+          RETURN NEW;
+      ELSIF TG_OP = 'UPDATE' THEN
+          IF (NEW.data->>'type' = 'Create') and (OLD.data->>'type' = 'Create') and activity_visibility(NEW.actor, NEW.recipients, NEW.data) != activity_visibility(OLD.actor, OLD.recipients, OLD.data) THEN
+             EXECUTE 'INSERT INTO counter_cache (name, count) VALUES (''status_visibility_' || activity_visibility(NEW.actor, NEW.recipients, NEW.data) || ''', 1) ON CONFLICT (name) DO UPDATE SET count = counter_cache.count + 1';
+             EXECUTE 'update counter_cache SET count = counter_cache.count - 1 where count > 0 and name = ''status_visibility_' || activity_visibility(OLD.actor, OLD.recipients, OLD.data) || ''';';
+          END IF;
+          RETURN NEW;
+      ELSIF TG_OP = 'DELETE' THEN
+          IF OLD.data->>'type' = 'Create' THEN
+            EXECUTE 'update counter_cache SET count = counter_cache.count - 1 where count > 0 and name = ''status_visibility_' || activity_visibility(OLD.actor, OLD.recipients, OLD.data) || ''';';
+          END IF;
+          RETURN OLD;
+      END IF;
+      END;
+    $$
+    LANGUAGE 'plpgsql';
+    """
+    |> execute()
+
+    """
+    CREATE TRIGGER #{@trigger_name} BEFORE INSERT OR UPDATE of recipients, data OR DELETE ON activities
+    FOR EACH ROW
+    EXECUTE PROCEDURE #{@function_name}();
+    """
+    |> execute()
+  end
+end
index 6227769dccc8081cce8a343109c3b9cce235dbd3..757afa129fb3ec7e5a83bd4fbc28cb786183abd5 100644 (file)
@@ -10,8 +10,8 @@ defmodule Pleroma.Repo.Migrations.AddFtsIndexToObjectsTwo do
 
     execute("CREATE FUNCTION objects_fts_update() RETURNS trigger AS $$
     begin
-      new.fts_content := to_tsvector('english', new.data->>'content');
-      return new;
+    new.fts_content := to_tsvector('english', new.data->>'content');
+    return new;
     end
     $$ LANGUAGE plpgsql")
     execute("create index if not exists objects_fts on objects using RUM (fts_content rum_tsvector_addon_ops, inserted_at) with (attach = 'inserted_at', to = 'fts_content');")
diff --git a/test/application_requirements_test.exs b/test/application_requirements_test.exs
new file mode 100644 (file)
index 0000000..481cdfd
--- /dev/null
@@ -0,0 +1,96 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.ApplicationRequirementsTest do
+  use Pleroma.DataCase
+  import ExUnit.CaptureLog
+  import Mock
+
+  alias Pleroma.Repo
+
+  describe "check_rum!" do
+    setup_with_mocks([
+      {Pleroma.ApplicationRequirements, [:passthrough],
+       [check_migrations_applied!: fn _ -> :ok end]}
+    ]) do
+      :ok
+    end
+
+    setup do: clear_config([:database, :rum_enabled])
+
+    test "raises if rum is enabled and detects unapplied rum migrations" do
+      Pleroma.Config.put([:database, :rum_enabled], true)
+
+      with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do
+        assert_raise Pleroma.ApplicationRequirements.VerifyError,
+                     "Unapplied RUM Migrations detected",
+                     fn ->
+                       capture_log(&Pleroma.ApplicationRequirements.verify!/0)
+                     end
+      end
+    end
+
+    test "raises if rum is disabled and detects rum migrations" do
+      Pleroma.Config.put([:database, :rum_enabled], false)
+
+      with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do
+        assert_raise Pleroma.ApplicationRequirements.VerifyError,
+                     "RUM Migrations detected",
+                     fn ->
+                       capture_log(&Pleroma.ApplicationRequirements.verify!/0)
+                     end
+      end
+    end
+
+    test "doesn't do anything if rum enabled and applied migrations" do
+      Pleroma.Config.put([:database, :rum_enabled], true)
+
+      with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do
+        assert Pleroma.ApplicationRequirements.verify!() == :ok
+      end
+    end
+
+    test "doesn't do anything if rum disabled" do
+      Pleroma.Config.put([:database, :rum_enabled], false)
+
+      with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do
+        assert Pleroma.ApplicationRequirements.verify!() == :ok
+      end
+    end
+  end
+
+  describe "check_migrations_applied!" do
+    setup_with_mocks([
+      {Ecto.Migrator, [],
+       [
+         with_repo: fn repo, fun -> passthrough([repo, fun]) end,
+         migrations: fn Repo ->
+           [
+             {:up, 20_191_128_153_944, "fix_missing_following_count"},
+             {:up, 20_191_203_043_610, "create_report_notes"},
+             {:down, 20_191_220_174_645, "add_scopes_to_pleroma_feo_auth_records"}
+           ]
+         end
+       ]}
+    ]) do
+      :ok
+    end
+
+    setup do: clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check])
+
+    test "raises if it detects unapplied migrations" do
+      assert_raise Pleroma.ApplicationRequirements.VerifyError,
+                   "Unapplied Migrations detected",
+                   fn ->
+                     capture_log(&Pleroma.ApplicationRequirements.verify!/0)
+                   end
+    end
+
+    test "doesn't do anything if disabled" do
+      Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true)
+
+      assert :ok == Pleroma.ApplicationRequirements.verify!()
+    end
+  end
+end
diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs
new file mode 100644 (file)
index 0000000..548ee87
--- /dev/null
@@ -0,0 +1,57 @@
+defmodule Pleroma.Config.DeprecationWarningsTest do
+  use ExUnit.Case, async: true
+  use Pleroma.Tests.Helpers
+
+  import ExUnit.CaptureLog
+
+  test "check_old_mrf_config/0" do
+    clear_config([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.NoOpPolicy)
+    clear_config([:instance, :mrf_transparency], true)
+    clear_config([:instance, :mrf_transparency_exclusions], [])
+
+    assert capture_log(fn -> Pleroma.Config.DeprecationWarnings.check_old_mrf_config() end) =~
+             """
+             !!!DEPRECATION WARNING!!!
+             Your config is using old namespaces for MRF configuration. They should work for now, but you are advised to change to new namespaces to prevent possible issues later:
+
+             * `config :pleroma, :instance, rewrite_policy` is now `config :pleroma, :mrf, policies`
+             * `config :pleroma, :instance, mrf_transparency` is now `config :pleroma, :mrf, transparency`
+             * `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`
+             """
+  end
+
+  test "move_namespace_and_warn/2" do
+    old_group1 = [:group, :key]
+    old_group2 = [:group, :key2]
+    old_group3 = [:group, :key3]
+
+    new_group1 = [:another_group, :key4]
+    new_group2 = [:another_group, :key5]
+    new_group3 = [:another_group, :key6]
+
+    clear_config(old_group1, 1)
+    clear_config(old_group2, 2)
+    clear_config(old_group3, 3)
+
+    clear_config(new_group1)
+    clear_config(new_group2)
+    clear_config(new_group3)
+
+    config_map = [
+      {old_group1, new_group1, "\n error :key"},
+      {old_group2, new_group2, "\n error :key2"},
+      {old_group3, new_group3, "\n error :key3"}
+    ]
+
+    assert capture_log(fn ->
+             Pleroma.Config.DeprecationWarnings.move_namespace_and_warn(
+               config_map,
+               "Warning preface"
+             )
+           end) =~ "Warning preface\n error :key\n error :key2\n error :key3"
+
+    assert Pleroma.Config.get(new_group1) == 1
+    assert Pleroma.Config.get(new_group2) == 2
+    assert Pleroma.Config.get(new_group3) == 3
+  end
+end
index dc950ca30833f65df60167482980bb08478391ad..fa8c7c7e85ef3ceefedc6d42f0c51d2f3ef340c2 100644 (file)
@@ -9,3 +9,5 @@ config :quack, level: :info
 config :pleroma, Pleroma.Repo, pool: Ecto.Adapters.SQL.Sandbox
 
 config :postgrex, :json_library, Poison
+
+config :pleroma, :database, rum_enabled: true
diff --git a/test/http/ex_aws_test.exs b/test/http/ex_aws_test.exs
new file mode 100644 (file)
index 0000000..d0b00ca
--- /dev/null
@@ -0,0 +1,54 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.HTTP.ExAwsTest do
+  use ExUnit.Case
+
+  import Tesla.Mock
+  alias Pleroma.HTTP
+
+  @url "https://s3.amazonaws.com/test_bucket/test_image.jpg"
+
+  setup do
+    mock(fn
+      %{method: :get, url: @url, headers: [{"x-amz-bucket-region", "us-east-1"}]} ->
+        %Tesla.Env{
+          status: 200,
+          body: "image-content",
+          headers: [{"x-amz-bucket-region", "us-east-1"}]
+        }
+
+      %{method: :post, url: @url, body: "image-content-2"} ->
+        %Tesla.Env{status: 200, body: "image-content-2"}
+    end)
+
+    :ok
+  end
+
+  describe "request" do
+    test "get" do
+      assert HTTP.ExAws.request(:get, @url, "", [{"x-amz-bucket-region", "us-east-1"}]) == {
+               :ok,
+               %{
+                 body: "image-content",
+                 headers: [{"x-amz-bucket-region", "us-east-1"}],
+                 status_code: 200
+               }
+             }
+    end
+
+    test "post" do
+      assert HTTP.ExAws.request(:post, @url, "image-content-2", [
+               {"x-amz-bucket-region", "us-east-1"}
+             ]) == {
+               :ok,
+               %{
+                 body: "image-content-2",
+                 headers: [],
+                 status_code: 200
+               }
+             }
+    end
+  end
+end
diff --git a/test/http/tzdata_test.exs b/test/http/tzdata_test.exs
new file mode 100644 (file)
index 0000000..3e605d3
--- /dev/null
@@ -0,0 +1,35 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.HTTP.TzdataTest do
+  use ExUnit.Case
+
+  import Tesla.Mock
+  alias Pleroma.HTTP
+  @url "https://data.iana.org/time-zones/tzdata-latest.tar.gz"
+
+  setup do
+    mock(fn
+      %{method: :head, url: @url} ->
+        %Tesla.Env{status: 200, body: ""}
+
+      %{method: :get, url: @url} ->
+        %Tesla.Env{status: 200, body: "hello"}
+    end)
+
+    :ok
+  end
+
+  describe "head/1" do
+    test "returns successfully result" do
+      assert HTTP.Tzdata.head(@url, [], []) == {:ok, {200, []}}
+    end
+  end
+
+  describe "get/1" do
+    test "returns successfully result" do
+      assert HTTP.Tzdata.get(@url, [], []) == {:ok, {200, [], "hello"}}
+    end
+  end
+end
index 618485b552c4547e4d00c2831ad42fe67a588d6d..d394bb94222770ef9217023e769a5e4888a1353c 100644 (file)
@@ -17,6 +17,9 @@ defmodule Pleroma.HTTPTest do
       } ->
         json(%{"my" => "data"})
 
+      %{method: :head, url: "http://example.com/hello"} ->
+        %Tesla.Env{status: 200, body: ""}
+
       %{method: :get, url: "http://example.com/hello"} ->
         %Tesla.Env{status: 200, body: "hello"}
 
@@ -27,6 +30,12 @@ defmodule Pleroma.HTTPTest do
     :ok
   end
 
+  describe "head/1" do
+    test "returns successfully result" do
+      assert HTTP.head("http://example.com/hello") == {:ok, %Tesla.Env{status: 200, body: ""}}
+    end
+  end
+
   describe "get/1" do
     test "returns successfully result" do
       assert HTTP.get("http://example.com/hello") == {
index daffc65427487492b2d5d4113f0889ece8648d9c..92e827c950fb68401b38d2327f8fdc00d963fe85 100644 (file)
@@ -4,9 +4,7 @@
 
 defmodule Pleroma.RepoTest do
   use Pleroma.DataCase
-  import ExUnit.CaptureLog
   import Pleroma.Factory
-  import Mock
 
   alias Pleroma.User
 
@@ -49,36 +47,4 @@ defmodule Pleroma.RepoTest do
       assert Repo.get_assoc(token, :user) == {:error, :not_found}
     end
   end
-
-  describe "check_migrations_applied!" do
-    setup_with_mocks([
-      {Ecto.Migrator, [],
-       [
-         with_repo: fn repo, fun -> passthrough([repo, fun]) end,
-         migrations: fn Pleroma.Repo ->
-           [
-             {:up, 20_191_128_153_944, "fix_missing_following_count"},
-             {:up, 20_191_203_043_610, "create_report_notes"},
-             {:down, 20_191_220_174_645, "add_scopes_to_pleroma_feo_auth_records"}
-           ]
-         end
-       ]}
-    ]) do
-      :ok
-    end
-
-    setup do: clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check])
-
-    test "raises if it detects unapplied migrations" do
-      assert_raise Pleroma.Repo.UnappliedMigrationsError, fn ->
-        capture_log(&Repo.check_migrations_applied!/0)
-      end
-    end
-
-    test "doesn't do anything if disabled" do
-      Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true)
-
-      assert :ok == Repo.check_migrations_applied!()
-    end
-  end
 end
index 4b76e2e782b12de65e9d9f94e343b58e28fc7239..f09d8d31a689f69fc52a211cc538695fbc599ede 100644 (file)
@@ -17,10 +17,11 @@ defmodule Pleroma.StatsTest do
     end
   end
 
-  describe "status visibility count" do
+  describe "status visibility sum count" do
     test "on new status" do
+      instance2 = "instance2.tld"
       user = insert(:user)
-      other_user = insert(:user)
+      other_user = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
 
       CommonAPI.post(user, %{visibility: "public", status: "hey"})
 
@@ -45,24 +46,24 @@ defmodule Pleroma.StatsTest do
         })
       end)
 
-      assert %{direct: 3, private: 4, public: 1, unlisted: 2} =
+      assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} =
                Pleroma.Stats.get_status_visibility_count()
     end
 
     test "on status delete" do
       user = insert(:user)
       {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
-      assert %{public: 1} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 1} = Pleroma.Stats.get_status_visibility_count()
       CommonAPI.delete(activity.id, user)
-      assert %{public: 0} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 0} = Pleroma.Stats.get_status_visibility_count()
     end
 
     test "on status visibility update" do
       user = insert(:user)
       {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
-      assert %{public: 1, private: 0} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 1, "private" => 0} = Pleroma.Stats.get_status_visibility_count()
       {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"})
-      assert %{public: 0, private: 1} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 0, "private" => 1} = Pleroma.Stats.get_status_visibility_count()
     end
 
     test "doesn't count unrelated activities" do
@@ -73,8 +74,46 @@ defmodule Pleroma.StatsTest do
       CommonAPI.favorite(other_user, activity.id)
       CommonAPI.repeat(activity.id, other_user)
 
-      assert %{direct: 0, private: 0, public: 1, unlisted: 0} =
+      assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} =
                Pleroma.Stats.get_status_visibility_count()
     end
   end
+
+  describe "status visibility by instance count" do
+    test "single instance" do
+      local_instance = Pleroma.Web.Endpoint.url() |> String.split("//") |> Enum.at(1)
+      instance2 = "instance2.tld"
+      user1 = insert(:user)
+      user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
+
+      CommonAPI.post(user1, %{visibility: "public", status: "hey"})
+
+      Enum.each(1..5, fn _ ->
+        CommonAPI.post(user1, %{
+          visibility: "unlisted",
+          status: "hey"
+        })
+      end)
+
+      Enum.each(1..10, fn _ ->
+        CommonAPI.post(user1, %{
+          visibility: "direct",
+          status: "hey @#{user2.nickname}"
+        })
+      end)
+
+      Enum.each(1..20, fn _ ->
+        CommonAPI.post(user2, %{
+          visibility: "private",
+          status: "hey"
+        })
+      end)
+
+      assert %{"direct" => 10, "private" => 0, "public" => 1, "unlisted" => 5} =
+               Pleroma.Stats.get_status_visibility_count(local_instance)
+
+      assert %{"direct" => 0, "private" => 20, "public" => 0, "unlisted" => 0} =
+               Pleroma.Stats.get_status_visibility_count(instance2)
+    end
+  end
 end
index e1bddfebf42172572a4a5a9524d08f04860c4c44..71f36c0e362c2fce32380079a40313cae3fc5f13 100644 (file)
@@ -50,6 +50,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
       config3 = ConfigDB.get_by_params(%{group: ":quack", key: ":level"})
       refute ConfigDB.get_by_params(%{group: ":pleroma", key: "Pleroma.Repo"})
       refute ConfigDB.get_by_params(%{group: ":postgrex", key: ":json_library"})
+      refute ConfigDB.get_by_params(%{group: ":pleroma", key: ":database"})
 
       assert config1.value == [key: "value", key2: [Repo]]
       assert config2.value == [key: "value2", key2: ["Activity"]]
@@ -120,14 +121,11 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
           federation_reachability_timeout_days: 7,
           federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],
           allow_relay: true,
-          rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
           public: true,
           quarantined_instances: [],
           managed_config: true,
           static_dir: "instance/static/",
           allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"],
-          mrf_transparency: true,
-          mrf_transparency_exclusions: [],
           autofollowed_nicknames: [],
           max_pinned_statuses: 1,
           attachment_links: false,
@@ -174,7 +172,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
         end
 
       assert file ==
-               "#{header}\n\nconfig :pleroma, :instance,\n  name: \"Pleroma\",\n  email: \"example@example.com\",\n  notify_email: \"noreply@example.com\",\n  description: \"A Pleroma instance, an alternative fediverse server\",\n  limit: 5000,\n  chat_limit: 5000,\n  remote_limit: 100_000,\n  upload_limit: 16_000_000,\n  avatar_upload_limit: 2_000_000,\n  background_upload_limit: 4_000_000,\n  banner_upload_limit: 4_000_000,\n  poll_limits: %{\n    max_expiration: 31_536_000,\n    max_option_chars: 200,\n    max_options: 20,\n    min_expiration: 0\n  },\n  registrations_open: true,\n  federating: true,\n  federation_incoming_replies_max_depth: 100,\n  federation_reachability_timeout_days: 7,\n  federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n  allow_relay: true,\n  rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,\n  public: true,\n  quarantined_instances: [],\n  managed_config: true,\n  static_dir: \"instance/static/\",\n  allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n  mrf_transparency: true,\n  mrf_transparency_exclusions: [],\n  autofollowed_nicknames: [],\n  max_pinned_statuses: 1,\n  attachment_links: false,\n  welcome_user_nickname: nil,\n  welcome_message: nil,\n  max_report_comment_size: 1000,\n  safe_dm_mentions: false,\n  healthcheck: false,\n  remote_post_retention_days: 90,\n  skip_thread_containment: true,\n  limit_to_local_content: :unauthenticated,\n  user_bio_length: 5000,\n  user_name_length: 100,\n  max_account_fields: 10,\n  max_remote_account_fields: 20,\n  account_field_name_length: 512,\n  account_field_value_length: 2048,\n  external_user_synchronization: true,\n  extended_nickname_format: true,\n  multi_factor_authentication: [\n    totp: [digits: 6, period: 30],\n    backup_codes: [number: 2, length: 6]\n  ]\n"
+               "#{header}\n\nconfig :pleroma, :instance,\n  name: \"Pleroma\",\n  email: \"example@example.com\",\n  notify_email: \"noreply@example.com\",\n  description: \"A Pleroma instance, an alternative fediverse server\",\n  limit: 5000,\n  chat_limit: 5000,\n  remote_limit: 100_000,\n  upload_limit: 16_000_000,\n  avatar_upload_limit: 2_000_000,\n  background_upload_limit: 4_000_000,\n  banner_upload_limit: 4_000_000,\n  poll_limits: %{\n    max_expiration: 31_536_000,\n    max_option_chars: 200,\n    max_options: 20,\n    min_expiration: 0\n  },\n  registrations_open: true,\n  federating: true,\n  federation_incoming_replies_max_depth: 100,\n  federation_reachability_timeout_days: 7,\n  federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n  allow_relay: true,\n  public: true,\n  quarantined_instances: [],\n  managed_config: true,\n  static_dir: \"instance/static/\",\n  allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n  autofollowed_nicknames: [],\n  max_pinned_statuses: 1,\n  attachment_links: false,\n  welcome_user_nickname: nil,\n  welcome_message: nil,\n  max_report_comment_size: 1000,\n  safe_dm_mentions: false,\n  healthcheck: false,\n  remote_post_retention_days: 90,\n  skip_thread_containment: true,\n  limit_to_local_content: :unauthenticated,\n  user_bio_length: 5000,\n  user_name_length: 100,\n  max_account_fields: 10,\n  max_remote_account_fields: 20,\n  account_field_name_length: 512,\n  account_field_value_length: 2048,\n  external_user_synchronization: true,\n  extended_nickname_format: true,\n  multi_factor_authentication: [\n    totp: [digits: 6, period: 30],\n    backup_codes: [number: 2, length: 6]\n  ]\n"
     end
   end
 end
index 851971a778ef42cbe1c6e5f24b2094a70d11668d..6a1a9ac1741a3d804afc69efd535ebfa071f00d7 100644 (file)
@@ -37,7 +37,7 @@ defmodule Mix.Tasks.Pleroma.RefreshCounterCacheTest do
 
     assert capture_io(fn -> Mix.Tasks.Pleroma.RefreshCounterCache.run([]) end) =~ "Done\n"
 
-    assert %{direct: 3, private: 4, public: 1, unlisted: 2} =
+    assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} =
              Pleroma.Stats.get_status_visibility_count()
   end
 end
index 311b6c68300a11d7880e79fdbf49577f30a67dcd..9b66f3f51db7cb2672b6f6680b59b26a0c3234ad 100644 (file)
@@ -199,6 +199,16 @@ defmodule Pleroma.UserTest do
     assert [^pending_follower] = User.get_follow_requests(locked)
   end
 
+  test "doesn't return follow requests for deactivated accounts" do
+    locked = insert(:user, locked: true)
+    pending_follower = insert(:user, %{deactivated: true})
+
+    CommonAPI.follow(pending_follower, locked)
+
+    assert true == pending_follower.deactivated
+    assert [] = User.get_follow_requests(locked)
+  end
+
   test "clears follow requests when requester is blocked" do
     followed = insert(:user, locked: true)
     follower = insert(:user)
index e490a5744fc75dace9a97e5c9a622e46cad8fb68..e722f7c04ee157c990365d5e741b7b6449b6c734 100644 (file)
@@ -536,6 +536,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
     end
 
+    @tag capture_log: true
     test "without valid signature, " <>
            "it only accepts Create activities and requires enabled federation",
          %{conn: conn} do
@@ -648,11 +649,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
     test "it accepts announces with to as string instead of array", %{conn: conn} do
       user = insert(:user)
 
+      {:ok, post} = CommonAPI.post(user, %{status: "hey"})
+      announcer = insert(:user, local: false)
+
       data = %{
         "@context" => "https://www.w3.org/ns/activitystreams",
-        "actor" => "http://mastodon.example.org/users/admin",
-        "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
-        "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
+        "actor" => announcer.ap_id,
+        "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity",
+        "object" => post.data["object"],
         "to" => "https://www.w3.org/ns/activitystreams#Public",
         "cc" => [user.ap_id],
         "type" => "Announce"
index 7693f6400d07524cedd0bed93b5a716365999b26..be7ab2ae4691a8bd8b747fec83e8b47e4ccb455d 100644 (file)
@@ -1092,52 +1092,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     end
   end
 
-  describe "update" do
-    setup do: clear_config([:instance, :max_pinned_statuses])
-
-    test "it creates an update activity with the new user data" do
-      user = insert(:user)
-      {:ok, user} = User.ensure_keys_present(user)
-      user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
-
-      {:ok, update} =
-        ActivityPub.update(%{
-          actor: user_data["id"],
-          to: [user.follower_address],
-          cc: [],
-          object: user_data
-        })
-
-      assert update.data["actor"] == user.ap_id
-      assert update.data["to"] == [user.follower_address]
-      assert embedded_object = update.data["object"]
-      assert embedded_object["id"] == user_data["id"]
-      assert embedded_object["type"] == user_data["type"]
-    end
-  end
-
-  test "returned pinned statuses" do
-    Config.put([:instance, :max_pinned_statuses], 3)
-    user = insert(:user)
-
-    {:ok, activity_one} = CommonAPI.post(user, %{status: "HI!!!"})
-    {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
-    {:ok, activity_three} = CommonAPI.post(user, %{status: "HI!!!"})
-
-    CommonAPI.pin(activity_one.id, user)
-    user = refresh_record(user)
-
-    CommonAPI.pin(activity_two.id, user)
-    user = refresh_record(user)
-
-    CommonAPI.pin(activity_three.id, user)
-    user = refresh_record(user)
-
-    activities = ActivityPub.fetch_user_activities(user, nil, %{pinned: true})
-
-    assert 3 = length(activities)
-  end
-
   describe "flag/1" do
     setup do
       reporter = insert(:user)
@@ -2055,11 +2009,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
   end
 
   describe "global activity expiration" do
-    setup do: clear_config([:instance, :rewrite_policy])
+    setup do: clear_config([:mrf, :policies])
 
     test "creates an activity expiration for local Create activities" do
       Pleroma.Config.put(
-        [:instance, :rewrite_policy],
+        [:mrf, :policies],
         Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
       )
 
index c941066f2ead00d882846807a5816925e096d731..a63b254233fc2ecd9254c7adff3eb733488538b1 100644 (file)
@@ -60,8 +60,6 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
   end
 
   describe "describe/0" do
-    setup do: clear_config([:instance, :rewrite_policy])
-
     test "it works as expected with noop policy" do
       expected = %{
         mrf_policies: ["NoOpPolicy"],
@@ -72,7 +70,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
     end
 
     test "it works as expected with mock policy" do
-      Pleroma.Config.put([:instance, :rewrite_policy], [MRFModuleMock])
+      clear_config([:mrf, :policies], [MRFModuleMock])
 
       expected = %{
         mrf_policies: ["MRFModuleMock"],
index 31224abe0b99f45a2eaea72780b77a49f8bad957..770a8dcf8e34a4a0e2116b47ec91bd4b92e46927 100644 (file)
@@ -622,4 +622,36 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
       assert {:actor, {"can not announce this object publicly", []}} in cng.errors
     end
   end
+
+  describe "updates" do
+    setup do
+      user = insert(:user)
+
+      object = %{
+        "id" => user.ap_id,
+        "name" => "A new name",
+        "summary" => "A new bio"
+      }
+
+      {:ok, valid_update, []} = Builder.update(user, object)
+
+      %{user: user, valid_update: valid_update}
+    end
+
+    test "validates a basic object", %{valid_update: valid_update} do
+      assert {:ok, _update, []} = ObjectValidator.validate(valid_update, [])
+    end
+
+    test "returns an error if the object can't be updated by the actor", %{
+      valid_update: valid_update
+    } do
+      other_user = insert(:user)
+
+      update =
+        valid_update
+        |> Map.put("actor", other_user.ap_id)
+
+      assert {:error, _cng} = ObjectValidator.validate(update, [])
+    end
+  end
 end
index 6bbbaae87c620aa0e9bb66e7e858087cb9f492a4..12c9ef1da6e2dd3efc893b81d922145eb18c68f5 100644 (file)
@@ -64,6 +64,31 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
     end
   end
 
+  describe "update users" do
+    setup do
+      user = insert(:user)
+      {:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"})
+      {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
+
+      %{user: user, update_data: update_data, update: update}
+    end
+
+    test "it updates the user", %{user: user, update: update} do
+      {:ok, _, _} = SideEffects.handle(update)
+      user = User.get_by_id(user.id)
+      assert user.name == "new name!"
+    end
+
+    test "it uses a given changeset to update", %{user: user, update: update} do
+      changeset = Ecto.Changeset.change(user, %{default_scope: "direct"})
+
+      assert user.default_scope == "public"
+      {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset)
+      user = User.get_by_id(user.id)
+      assert user.default_scope == "direct"
+    end
+  end
+
   describe "delete objects" do
     setup do
       user = insert(:user)
diff --git a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs
new file mode 100644 (file)
index 0000000..6463665
--- /dev/null
@@ -0,0 +1,159 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Activity
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.Transmogrifier
+
+  import Pleroma.Factory
+
+  test "it works for incoming update activities" do
+    user = insert(:user, local: false)
+
+    update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
+
+    object =
+      update_data["object"]
+      |> Map.put("actor", user.ap_id)
+      |> Map.put("id", user.ap_id)
+
+    update_data =
+      update_data
+      |> Map.put("actor", user.ap_id)
+      |> Map.put("object", object)
+
+    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
+
+    assert data["id"] == update_data["id"]
+
+    user = User.get_cached_by_ap_id(data["actor"])
+    assert user.name == "gargle"
+
+    assert user.avatar["url"] == [
+             %{
+               "href" =>
+                 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
+             }
+           ]
+
+    assert user.banner["url"] == [
+             %{
+               "href" =>
+                 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
+             }
+           ]
+
+    assert user.bio == "<p>Some bio</p>"
+  end
+
+  test "it works with alsoKnownAs" do
+    %{ap_id: actor} = insert(:user, local: false)
+
+    assert User.get_cached_by_ap_id(actor).also_known_as == []
+
+    {:ok, _activity} =
+      "test/fixtures/mastodon-update.json"
+      |> File.read!()
+      |> Poison.decode!()
+      |> Map.put("actor", actor)
+      |> Map.update!("object", fn object ->
+        object
+        |> Map.put("actor", actor)
+        |> Map.put("id", actor)
+        |> Map.put("alsoKnownAs", [
+          "http://mastodon.example.org/users/foo",
+          "http://example.org/users/bar"
+        ])
+      end)
+      |> Transmogrifier.handle_incoming()
+
+    assert User.get_cached_by_ap_id(actor).also_known_as == [
+             "http://mastodon.example.org/users/foo",
+             "http://example.org/users/bar"
+           ]
+  end
+
+  test "it works with custom profile fields" do
+    user = insert(:user, local: false)
+
+    assert user.fields == []
+
+    update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
+
+    object =
+      update_data["object"]
+      |> Map.put("actor", user.ap_id)
+      |> Map.put("id", user.ap_id)
+
+    update_data =
+      update_data
+      |> Map.put("actor", user.ap_id)
+      |> Map.put("object", object)
+
+    {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data)
+
+    user = User.get_cached_by_ap_id(user.ap_id)
+
+    assert user.fields == [
+             %{"name" => "foo", "value" => "updated"},
+             %{"name" => "foo1", "value" => "updated"}
+           ]
+
+    Pleroma.Config.put([:instance, :max_remote_account_fields], 2)
+
+    update_data =
+      update_data
+      |> put_in(["object", "attachment"], [
+        %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"},
+        %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"},
+        %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"}
+      ])
+      |> Map.put("id", update_data["id"] <> ".")
+
+    {:ok, _} = Transmogrifier.handle_incoming(update_data)
+
+    user = User.get_cached_by_ap_id(user.ap_id)
+
+    assert user.fields == [
+             %{"name" => "foo", "value" => "updated"},
+             %{"name" => "foo1", "value" => "updated"}
+           ]
+
+    update_data =
+      update_data
+      |> put_in(["object", "attachment"], [])
+      |> Map.put("id", update_data["id"] <> ".")
+
+    {:ok, _} = Transmogrifier.handle_incoming(update_data)
+
+    user = User.get_cached_by_ap_id(user.ap_id)
+
+    assert user.fields == []
+  end
+
+  test "it works for incoming update activities which lock the account" do
+    user = insert(:user, local: false)
+
+    update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
+
+    object =
+      update_data["object"]
+      |> Map.put("actor", user.ap_id)
+      |> Map.put("id", user.ap_id)
+      |> Map.put("manuallyApprovesFollowers", true)
+
+    update_data =
+      update_data
+      |> Map.put("actor", user.ap_id)
+      |> Map.put("object", object)
+
+    {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(update_data)
+
+    user = User.get_cached_by_ap_id(user.ap_id)
+    assert user.locked == true
+  end
+end
index 47d6e843a63f71a22a7d13e840de1c6298bcbac0..100821056203140c700efd960336b8a9337824e9 100644 (file)
@@ -401,162 +401,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute Map.has_key?(object_data, "reaction_count")
     end
 
-    test "it works for incoming update activities" do
-      data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
-
-      object =
-        update_data["object"]
-        |> Map.put("actor", data["actor"])
-        |> Map.put("id", data["actor"])
-
-      update_data =
-        update_data
-        |> Map.put("actor", data["actor"])
-        |> Map.put("object", object)
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
-
-      assert data["id"] == update_data["id"]
-
-      user = User.get_cached_by_ap_id(data["actor"])
-      assert user.name == "gargle"
-
-      assert user.avatar["url"] == [
-               %{
-                 "href" =>
-                   "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
-               }
-             ]
-
-      assert user.banner["url"] == [
-               %{
-                 "href" =>
-                   "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
-               }
-             ]
-
-      assert user.bio == "<p>Some bio</p>"
-    end
-
-    test "it works with alsoKnownAs" do
-      {:ok, %Activity{data: %{"actor" => actor}}} =
-        "test/fixtures/mastodon-post-activity.json"
-        |> File.read!()
-        |> Poison.decode!()
-        |> Transmogrifier.handle_incoming()
-
-      assert User.get_cached_by_ap_id(actor).also_known_as == ["http://example.org/users/foo"]
-
-      {:ok, _activity} =
-        "test/fixtures/mastodon-update.json"
-        |> File.read!()
-        |> Poison.decode!()
-        |> Map.put("actor", actor)
-        |> Map.update!("object", fn object ->
-          object
-          |> Map.put("actor", actor)
-          |> Map.put("id", actor)
-          |> Map.put("alsoKnownAs", [
-            "http://mastodon.example.org/users/foo",
-            "http://example.org/users/bar"
-          ])
-        end)
-        |> Transmogrifier.handle_incoming()
-
-      assert User.get_cached_by_ap_id(actor).also_known_as == [
-               "http://mastodon.example.org/users/foo",
-               "http://example.org/users/bar"
-             ]
-    end
-
-    test "it works with custom profile fields" do
-      {:ok, activity} =
-        "test/fixtures/mastodon-post-activity.json"
-        |> File.read!()
-        |> Poison.decode!()
-        |> Transmogrifier.handle_incoming()
-
-      user = User.get_cached_by_ap_id(activity.actor)
-
-      assert user.fields == [
-               %{"name" => "foo", "value" => "bar"},
-               %{"name" => "foo1", "value" => "bar1"}
-             ]
-
-      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
-
-      object =
-        update_data["object"]
-        |> Map.put("actor", user.ap_id)
-        |> Map.put("id", user.ap_id)
-
-      update_data =
-        update_data
-        |> Map.put("actor", user.ap_id)
-        |> Map.put("object", object)
-
-      {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data)
-
-      user = User.get_cached_by_ap_id(user.ap_id)
-
-      assert user.fields == [
-               %{"name" => "foo", "value" => "updated"},
-               %{"name" => "foo1", "value" => "updated"}
-             ]
-
-      Pleroma.Config.put([:instance, :max_remote_account_fields], 2)
-
-      update_data =
-        put_in(update_data, ["object", "attachment"], [
-          %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"},
-          %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"},
-          %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"}
-        ])
-
-      {:ok, _} = Transmogrifier.handle_incoming(update_data)
-
-      user = User.get_cached_by_ap_id(user.ap_id)
-
-      assert user.fields == [
-               %{"name" => "foo", "value" => "updated"},
-               %{"name" => "foo1", "value" => "updated"}
-             ]
-
-      update_data = put_in(update_data, ["object", "attachment"], [])
-
-      {:ok, _} = Transmogrifier.handle_incoming(update_data)
-
-      user = User.get_cached_by_ap_id(user.ap_id)
-
-      assert user.fields == []
-    end
-
-    test "it works for incoming update activities which lock the account" do
-      data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
-
-      object =
-        update_data["object"]
-        |> Map.put("actor", data["actor"])
-        |> Map.put("id", data["actor"])
-        |> Map.put("manuallyApprovesFollowers", true)
-
-      update_data =
-        update_data
-        |> Map.put("actor", data["actor"])
-        |> Map.put("object", object)
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
-
-      user = User.get_cached_by_ap_id(data["actor"])
-      assert user.locked == true
-    end
-
     test "it works for incomming unfollows with an existing follow" do
       user = insert(:user)
 
index 3a3eb822da48af1e593d6b596941e875e8283343..48fb108ec83ee5c14d6b9a4c3490f589b1884679 100644 (file)
@@ -1732,6 +1732,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
                response["status_visibility"]
     end
+
+    test "by instance", %{conn: conn} do
+      admin = insert(:user, is_admin: true)
+      user1 = insert(:user)
+      instance2 = "instance2.tld"
+      user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
+
+      CommonAPI.post(user1, %{visibility: "public", status: "hey"})
+      CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"})
+      CommonAPI.post(user2, %{visibility: "private", status: "hey"})
+
+      response =
+        conn
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/stats", instance: instance2)
+        |> json_response(200)
+
+      assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
+               response["status_visibility"]
+    end
   end
 end
 
index de90aa6e055af94b277cb8eded78a590c3104e47..592fdccd19168a86f3c42ff95f448a5c13df977a 100644 (file)
@@ -23,7 +23,7 @@ defmodule Pleroma.Web.FederatorTest do
 
   setup_all do: clear_config([:instance, :federating], true)
   setup do: clear_config([:instance, :allow_relay])
-  setup do: clear_config([:instance, :rewrite_policy])
+  setup do: clear_config([:mrf, :policies])
   setup do: clear_config([:mrf_keyword])
 
   describe "Publish an activity" do
@@ -158,7 +158,7 @@ defmodule Pleroma.Web.FederatorTest do
       Pleroma.Config.put([:mrf_keyword, :reject], ["lain"])
 
       Pleroma.Config.put(
-        [:instance, :rewrite_policy],
+        [:mrf, :policies],
         Pleroma.Web.ActivityPub.MRF.KeywordPolicy
       )
 
index 00925caad9b14344ca24b621059137118df2daff..06b33607fb4ace57cfe545e8976e6285a6c2aa8b 100644 (file)
@@ -67,10 +67,10 @@ defmodule Pleroma.Web.NodeInfoTest do
   end
 
   test "returns fieldsLimits field", %{conn: conn} do
-    Config.put([:instance, :max_account_fields], 10)
-    Config.put([:instance, :max_remote_account_fields], 15)
-    Config.put([:instance, :account_field_name_length], 255)
-    Config.put([:instance, :account_field_value_length], 2048)
+    clear_config([:instance, :max_account_fields], 10)
+    clear_config([:instance, :max_remote_account_fields], 15)
+    clear_config([:instance, :account_field_name_length], 255)
+    clear_config([:instance, :account_field_value_length], 2048)
 
     response =
       conn
@@ -84,8 +84,7 @@ defmodule Pleroma.Web.NodeInfoTest do
   end
 
   test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
-    option = Config.get([:instance, :safe_dm_mentions])
-    Config.put([:instance, :safe_dm_mentions], true)
+    clear_config([:instance, :safe_dm_mentions], true)
 
     response =
       conn
@@ -102,8 +101,6 @@ defmodule Pleroma.Web.NodeInfoTest do
       |> json_response(:ok)
 
     refute "safe_dm_mentions" in response["metadata"]["features"]
-
-    Config.put([:instance, :safe_dm_mentions], option)
   end
 
   describe "`metadata/federation/enabled`" do
@@ -156,14 +153,11 @@ defmodule Pleroma.Web.NodeInfoTest do
   end
 
   test "it shows MRF transparency data if enabled", %{conn: conn} do
-    config = Config.get([:instance, :rewrite_policy])
-    Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
-
-    option = Config.get([:instance, :mrf_transparency])
-    Config.put([:instance, :mrf_transparency], true)
+    clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
+    clear_config([:mrf, :transparency], true)
 
     simple_config = %{"reject" => ["example.com"]}
-    Config.put(:mrf_simple, simple_config)
+    clear_config(:mrf_simple, simple_config)
 
     response =
       conn
@@ -171,26 +165,17 @@ defmodule Pleroma.Web.NodeInfoTest do
       |> json_response(:ok)
 
     assert response["metadata"]["federation"]["mrf_simple"] == simple_config
-
-    Config.put([:instance, :rewrite_policy], config)
-    Config.put([:instance, :mrf_transparency], option)
-    Config.put(:mrf_simple, %{})
   end
 
   test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do
-    config = Config.get([:instance, :rewrite_policy])
-    Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
-
-    option = Config.get([:instance, :mrf_transparency])
-    Config.put([:instance, :mrf_transparency], true)
-
-    exclusions = Config.get([:instance, :mrf_transparency_exclusions])
-    Config.put([:instance, :mrf_transparency_exclusions], ["other.site"])
+    clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
+    clear_config([:mrf, :transparency], true)
+    clear_config([:mrf, :transparency_exclusions], ["other.site"])
 
     simple_config = %{"reject" => ["example.com", "other.site"]}
-    expected_config = %{"reject" => ["example.com"]}
+    clear_config(:mrf_simple, simple_config)
 
-    Config.put(:mrf_simple, simple_config)
+    expected_config = %{"reject" => ["example.com"]}
 
     response =
       conn
@@ -199,10 +184,5 @@ defmodule Pleroma.Web.NodeInfoTest do
 
     assert response["metadata"]["federation"]["mrf_simple"] == expected_config
     assert response["metadata"]["federation"]["exclusions"] == true
-
-    Config.put([:instance, :rewrite_policy], config)
-    Config.put([:instance, :mrf_transparency], option)
-    Config.put([:instance, :mrf_transparency_exclusions], exclusions)
-    Config.put(:mrf_simple, %{})
   end
 end
index 6d2991a6071d3b19ea5aab0d787fa86a2f007a9f..b1db59fdf8876eb89ae3d64a715711af70365b69 100644 (file)
@@ -13,7 +13,6 @@ defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorkerTest do
 
   setup do
     clear_config([ActivityExpiration, :enabled])
-    clear_config([:instance, :rewrite_policy])
   end
 
   test "deletes an expiration activity" do
@@ -42,10 +41,7 @@ defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorkerTest do
   test "works with ActivityExpirationPolicy" do
     Pleroma.Config.put([ActivityExpiration, :enabled], true)
 
-    Pleroma.Config.put(
-      [:instance, :rewrite_policy],
-      Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
-    )
+    clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy)
 
     user = insert(:user)