Merge branch 'account-notes' into 'develop'
authorAlex Gleason <alex@alexgleason.me>
Sat, 25 Dec 2021 01:41:12 +0000 (01:41 +0000)
committerAlex Gleason <alex@alexgleason.me>
Sat, 25 Dec 2021 01:41:12 +0000 (01:41 +0000)
MastoAPI: Add user notes on accounts

See merge request pleroma/pleroma!3540

68 files changed:
CHANGELOG.md
Dockerfile
benchmarks/load_testing/activities.ex
benchmarks/mix/tasks/pleroma/benchmarks/tags.ex
benchmarks/mix/tasks/pleroma/benchmarks/timelines.ex
config/benchmark.exs
config/config.exs
docs/development/API/admin_api.md
docs/development/API/nodeinfo.md [new file with mode: 0644]
docs/development/API/pleroma_api.md
lib/mix/tasks/pleroma/instance.ex
lib/pleroma/activity.ex
lib/pleroma/ecto_enums.ex
lib/pleroma/emoji/loader.ex
lib/pleroma/moderation_log.ex
lib/pleroma/user.ex
lib/pleroma/user/query.ex
lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex
lib/pleroma/web/activity_pub/publisher.ex
lib/pleroma/web/activity_pub/side_effects.ex
lib/pleroma/web/activity_pub/utils.ex
lib/pleroma/web/admin_api/controllers/user_controller.ex
lib/pleroma/web/admin_api/views/account_view.ex
lib/pleroma/web/api_spec/operations/account_operation.ex
lib/pleroma/web/api_spec/operations/admin/user_operation.ex
lib/pleroma/web/api_spec/operations/twitter_util_operation.ex
lib/pleroma/web/api_spec/schemas/account.ex
lib/pleroma/web/api_spec/schemas/account_relationship.ex
lib/pleroma/web/api_spec/schemas/status.ex
lib/pleroma/web/endpoint.ex
lib/pleroma/web/manifest_controller.ex [new file with mode: 0644]
lib/pleroma/web/mastodon_api/controllers/search_controller.ex
lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
lib/pleroma/web/mastodon_api/mastodon_api.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/mastodon_api/views/instance_view.ex
lib/pleroma/web/mastodon_api/views/suggestion_view.ex [new file with mode: 0644]
lib/pleroma/web/nodeinfo/nodeinfo.ex
lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/controllers/password_controller.ex
lib/pleroma/web/twitter_api/controllers/util_controller.ex
lib/pleroma/web/views/manifest_view.ex [new file with mode: 0644]
mix.exs
mix.lock
priv/repo/migrations/20211125110126_force_pinned_objects_to_exist.exs [new file with mode: 0644]
priv/repo/migrations/20211126191138_add_suggestions.exs [new file with mode: 0644]
priv/templates/sample_config.eex
test/pleroma/config/transfer_task_test.exs
test/pleroma/user/query_test.exs
test/pleroma/user_test.exs
test/pleroma/web/activity_pub/activity_pub_test.exs
test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs
test/pleroma/web/activity_pub/side_effects_test.exs
test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs
test/pleroma/web/activity_pub/transmogrifier_test.exs
test/pleroma/web/activity_pub/utils_test.exs
test/pleroma/web/admin_api/controllers/user_controller_test.exs
test/pleroma/web/manifest_controller_test.exs [new file with mode: 0644]
test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs
test/pleroma/web/mastodon_api/views/account_view_test.exs
test/pleroma/web/mastodon_api/views/suggestion_view_test.exs [new file with mode: 0644]
test/pleroma/web/plugs/frontend_static_plug_test.exs
test/pleroma/web/plugs/rate_limiter_test.exs
test/pleroma/web/twitter_api/password_controller_test.exs
test/pleroma/web/twitter_api/util_controller_test.exs
test/test_helper.exs

index decf9ef471a74d66b96b7576f73856204deab9cc..8e97da1896ab9467f0bf9637588821c2c6d1b536 100644 (file)
@@ -14,9 +14,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Allow users to remove their emails if instance does not need email to register
 
 ### Added
+- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
 
 ### Fixed
 - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
+- Handle Reject for already-accepted Follows properly
 
 ### Removed
 
@@ -64,6 +66,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Attachment dimensions and blurhashes are federated when available.
 - Mastodon API: support `poll` notification.
 - Pinned posts federation
+- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
 
 ### Fixed
 - Don't crash so hard when email settings are invalid.
index db1a6b45726ee35815f85a8926377eda9240df09..c51ebbab04755d161d1f1305b0ad8a4112f28a49 100644 (file)
@@ -12,7 +12,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev &&\
        mkdir release &&\
        mix release --path release
 
-FROM alpine:3.11
+FROM alpine:3.14
 
 ARG BUILD_DATE
 ARG VCS_REF
@@ -31,8 +31,7 @@ LABEL maintainer="ops@pleroma.social" \
 ARG HOME=/opt/pleroma
 ARG DATA=/var/lib/pleroma
 
-RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
-       apk update &&\
+RUN apk update &&\
        apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
        adduser --system --shell /bin/false --home ${HOME} pleroma &&\
        mkdir -p ${DATA}/uploads &&\
index b9f6b24da5a5cd79f43ef0e4a416e31e12c1e7d1..7f262d228cc9ebf8a5222df45446702b8342e855 100644 (file)
@@ -394,7 +394,7 @@ defmodule Pleroma.LoadTesting.Activities do
 
   defp other_data(actor, content) do
     %{host: host} = URI.parse(actor.ap_id)
-    datetime = DateTime.utc_now()
+    datetime = DateTime.utc_now() |> to_string()
     context_id = "https://#{host}/contexts/#{UUID.generate()}"
     activity_id = "https://#{host}/activities/#{UUID.generate()}"
     object_id = "https://#{host}/objects/#{UUID.generate()}"
index c051335a5ab644ee738673a347877223d668a128..a32de2db4d969c1a3260b28f74be08a8ff5bb5be 100644 (file)
@@ -99,15 +99,16 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
       |> Enum.map(&String.downcase(&1))
 
     _activities =
-      params
-      |> Map.put(:type, "Create")
-      |> Map.put(:local_only, local_only)
-      |> Map.put(:blocking_user, user)
-      |> Map.put(:muting_user, user)
-      |> Map.put(:user, user)
-      |> Map.put(:tag, tags)
-      |> Map.put(:tag_all, tag_all)
-      |> Map.put(:tag_reject, tag_reject)
+      %{
+        type: "Create",
+        local_only: local_only,
+        blocking_user: user,
+        muting_user: user,
+        user: user,
+        tag: tags,
+        tag_all: tag_all,
+        tag_reject: tag_reject,
+      }
       |> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
   end
 end
index aed32f1946a58e5fc28e864d4e4103da169de729..3770ca163b541e6cc50da4404c8dbb6726d09d74 100644 (file)
@@ -17,14 +17,14 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines do
     # Let the user make 100 posts
 
     1..100
-    |> Enum.each(fn i -> CommonAPI.post(user, %{"status" => to_string(i)}) end)
+    |> Enum.each(fn i -> CommonAPI.post(user, %{status: to_string(i)}) end)
 
     # Let 10 random users post
     posts =
       users
       |> Enum.take_random(10)
       |> Enum.map(fn {:ok, random_user} ->
-        {:ok, activity} = CommonAPI.post(random_user, %{"status" => "."})
+        {:ok, activity} = CommonAPI.post(random_user, %{status: "."})
         activity
       end)
 
@@ -42,7 +42,7 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines do
             |> Conn.assign(:user, reading_user)
             |> Conn.assign(:skip_link_headers, true)
 
-          Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
+          Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
         end
       },
       inputs: %{"user" => user, "no user" => nil},
@@ -50,7 +50,7 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines do
     )
 
     users
-    |> Enum.each(fn {:ok, follower, user} -> Pleroma.User.follow(follower, user) end)
+    |> Enum.each(fn {:ok, follower} -> Pleroma.User.follow(follower, user) end)
 
     Benchee.run(
       %{
@@ -60,7 +60,7 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines do
             |> Conn.assign(:user, reading_user)
             |> Conn.assign(:skip_link_headers, true)
 
-          Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
+          Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
         end
       },
       inputs: %{"user" => user, "no user" => nil},
index a4d048f1bb0a91dc89daeb5812a73ffe456f23b7..9a7ea566915bbe336e3e9685c6c8c8e21aebb3f5 100644 (file)
@@ -4,8 +4,7 @@ import Config
 # you can enable the server option below.
 config :pleroma, Pleroma.Web.Endpoint,
   http: [port: 4001],
-  url: [port: 4001],
-  server: true
+  url: [port: 4001]
 
 # Disable captha for tests
 config :pleroma, Pleroma.Captcha,
@@ -44,7 +43,7 @@ config :pleroma, Pleroma.Repo,
   pool_size: 10
 
 # Reduce hash rounds for testing
-config :pbkdf2_elixir, rounds: 1
+config :pleroma, :password, iterations: 1
 
 config :tesla, adapter: Tesla.Mock
 
index 681b498275d90bdc80ebb5bd28b6100075136a54..c9592511f229dc6cd51061904e59bcb9b197ffd8 100644 (file)
@@ -139,6 +139,7 @@ config :pleroma, Pleroma.Web.Endpoint,
   ],
   protocol: "https",
   secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
+  live_view: [signing_salt: "U5ELgdEwTD3n1+D5s0rY0AMg8/y1STxZ3Zvsl3bWh+oBcGrYdil0rXqPMRd3Glcq"],
   signing_salt: "CqaoopA2",
   render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
   pubsub_server: Pleroma.PubSub,
index 82483fae71deaf9f82c8ff6afb3ef2f913183083..f140818934a05fe41761a9f65ce92d4765357e18 100644 (file)
@@ -261,6 +261,46 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 }
 ```
 
+## `PATCH /api/v1/pleroma/admin/users/suggest`
+
+### Suggest a user
+
+Adds the user(s) to follower recommendations.
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/unsuggest`
+
+### Unsuggest a user
+
+Removes the user(s) from follower recommendations.
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
 ## `GET /api/v1/pleroma/admin/users/:nickname_or_id`
 
 ### Retrive the details of a user
diff --git a/docs/development/API/nodeinfo.md b/docs/development/API/nodeinfo.md
new file mode 100644 (file)
index 0000000..0f998a1
--- /dev/null
@@ -0,0 +1,347 @@
+# Nodeinfo
+
+See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/).
+
+## `/.well-known/nodeinfo`
+### The well-known path
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+```json
+{
+   "links":[
+      {
+         "href":"https://example.com/nodeinfo/2.0.json",
+         "rel":"http://nodeinfo.diaspora.software/ns/schema/2.0"
+      },
+      {
+         "href":"https://example.com/nodeinfo/2.1.json",
+         "rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"
+      }
+   ]
+}
+```
+
+## `/nodeinfo/2.0.json`
+### Nodeinfo 2.0
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+```json
+{
+   "metadata":{
+      "accountActivationRequired":false,
+      "features":[
+         "pleroma_api",
+         "mastodon_api",
+         "mastodon_api_streaming",
+         "polls",
+         "pleroma_explicit_addressing",
+         "shareable_emoji_packs",
+         "multifetch",
+         "pleroma:api/v1/notifications:include_types_filter",
+         "chat",
+         "shout",
+         "relay",
+         "pleroma_emoji_reactions",
+         "pleroma_chat_messages"
+      ],
+      "federation":{
+         "enabled":true,
+         "exclusions":false,
+         "mrf_hashtag":{
+            "federated_timeline_removal":[
+               
+            ],
+            "reject":[
+               
+            ],
+            "sensitive":[
+               "nsfw"
+            ]
+         },
+         "mrf_object_age":{
+            "actions":[
+               "delist",
+               "strip_followers"
+            ],
+            "threshold":604800
+         },
+         "mrf_policies":[
+            "ObjectAgePolicy",
+            "TagPolicy",
+            "HashtagPolicy"
+         ],
+         "quarantined_instances":[
+            
+         ]
+      },
+      "fieldsLimits":{
+         "maxFields":10,
+         "maxRemoteFields":20,
+         "nameLength":512,
+         "valueLength":2048
+      },
+      "invitesEnabled":false,
+      "mailerEnabled":false,
+      "nodeDescription":"Pleroma: An efficient and flexible fediverse server",
+      "nodeName":"Example",
+      "pollLimits":{
+         "max_expiration":31536000,
+         "max_option_chars":200,
+         "max_options":20,
+         "min_expiration":0
+      },
+      "postFormats":[
+         "text/plain",
+         "text/html",
+         "text/markdown",
+         "text/bbcode"
+      ],
+      "private":false,
+      "restrictedNicknames":[
+         ".well-known",
+         "~",
+         "about",
+         "activities",
+         "api",
+         "auth",
+         "check_password",
+         "dev",
+         "friend-requests",
+         "inbox",
+         "internal",
+         "main",
+         "media",
+         "nodeinfo",
+         "notice",
+         "oauth",
+         "objects",
+         "ostatus_subscribe",
+         "pleroma",
+         "proxy",
+         "push",
+         "registration",
+         "relay",
+         "settings",
+         "status",
+         "tag",
+         "user-search",
+         "user_exists",
+         "users",
+         "web",
+         "verify_credentials",
+         "update_credentials",
+         "relationships",
+         "search",
+         "confirmation_resend",
+         "mfa"
+      ],
+      "skipThreadContainment":true,
+      "staffAccounts":[
+         "https://example.com/users/admin",
+         "https://example.com/users/staff"
+      ],
+      "suggestions":{
+         "enabled":false
+      },
+      "uploadLimits":{
+         "avatar":2000000,
+         "background":4000000,
+         "banner":4000000,
+         "general":16000000
+      }
+   },
+   "openRegistrations":true,
+   "protocols":[
+      "activitypub"
+   ],
+   "services":{
+      "inbound":[
+         
+      ],
+      "outbound":[
+         
+      ]
+   },
+   "software":{
+      "name":"pleroma",
+      "version":"2.4.1"
+   },
+   "usage":{
+      "localPosts":27,
+      "users":{
+         "activeHalfyear":129,
+         "activeMonth":70,
+         "total":235
+      }
+   },
+   "version":"2.0"
+}
+```
+
+## `/nodeinfo/2.1.json`
+### Nodeinfo 2.1
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+```json
+{
+   "metadata":{
+      "accountActivationRequired":false,
+      "features":[
+         "pleroma_api",
+         "mastodon_api",
+         "mastodon_api_streaming",
+         "polls",
+         "pleroma_explicit_addressing",
+         "shareable_emoji_packs",
+         "multifetch",
+         "pleroma:api/v1/notifications:include_types_filter",
+         "chat",
+         "shout",
+         "relay",
+         "pleroma_emoji_reactions",
+         "pleroma_chat_messages"
+      ],
+      "federation":{
+         "enabled":true,
+         "exclusions":false,
+         "mrf_hashtag":{
+            "federated_timeline_removal":[
+               
+            ],
+            "reject":[
+               
+            ],
+            "sensitive":[
+               "nsfw"
+            ]
+         },
+         "mrf_object_age":{
+            "actions":[
+               "delist",
+               "strip_followers"
+            ],
+            "threshold":604800
+         },
+         "mrf_policies":[
+            "ObjectAgePolicy",
+            "TagPolicy",
+            "HashtagPolicy"
+         ],
+         "quarantined_instances":[
+            
+         ]
+      },
+      "fieldsLimits":{
+         "maxFields":10,
+         "maxRemoteFields":20,
+         "nameLength":512,
+         "valueLength":2048
+      },
+      "invitesEnabled":false,
+      "mailerEnabled":false,
+      "nodeDescription":"Pleroma: An efficient and flexible fediverse server",
+      "nodeName":"Example",
+      "pollLimits":{
+         "max_expiration":31536000,
+         "max_option_chars":200,
+         "max_options":20,
+         "min_expiration":0
+      },
+      "postFormats":[
+         "text/plain",
+         "text/html",
+         "text/markdown",
+         "text/bbcode"
+      ],
+      "private":false,
+      "restrictedNicknames":[
+         ".well-known",
+         "~",
+         "about",
+         "activities",
+         "api",
+         "auth",
+         "check_password",
+         "dev",
+         "friend-requests",
+         "inbox",
+         "internal",
+         "main",
+         "media",
+         "nodeinfo",
+         "notice",
+         "oauth",
+         "objects",
+         "ostatus_subscribe",
+         "pleroma",
+         "proxy",
+         "push",
+         "registration",
+         "relay",
+         "settings",
+         "status",
+         "tag",
+         "user-search",
+         "user_exists",
+         "users",
+         "web",
+         "verify_credentials",
+         "update_credentials",
+         "relationships",
+         "search",
+         "confirmation_resend",
+         "mfa"
+      ],
+      "skipThreadContainment":true,
+      "staffAccounts":[
+         "https://example.com/users/admin",
+         "https://example.com/users/staff"
+      ],
+      "suggestions":{
+         "enabled":false
+      },
+      "uploadLimits":{
+         "avatar":2000000,
+         "background":4000000,
+         "banner":4000000,
+         "general":16000000
+      }
+   },
+   "openRegistrations":true,
+   "protocols":[
+      "activitypub"
+   ],
+   "services":{
+      "inbound":[
+         
+      ],
+      "outbound":[
+         
+      ]
+   },
+   "software":{
+      "name":"pleroma",
+      "repository":"https://git.pleroma.social/pleroma/pleroma",
+      "version":"2.4.1"
+   },
+   "usage":{
+      "localPosts":27,
+      "users":{
+         "activeHalfyear":129,
+         "activeMonth":70,
+         "total":235
+      }
+   },
+   "version":"2.1"
+}
+```
+
index b401a7cc74b0ff3608c1d8596e6c4cd1d0542da5..0e7367a726fb086875295684c1c6fdbeb75acfe3 100644 (file)
@@ -159,6 +159,7 @@ See [Admin-API](admin_api.md)
   "muting": false,
   "muting_notifications": false,
   "subscribing": true,
+  "notifying": true,
   "requested": false,
   "domain_blocking": false,
   "showing_reblogs": true,
@@ -184,6 +185,7 @@ See [Admin-API](admin_api.md)
   "muting": false,
   "muting_notifications": false,
   "subscribing": false,
+  "notifying": false,
   "requested": false,
   "domain_blocking": false,
   "showing_reblogs": true,
index da27a99d0e7b4efc0be49c35fb7fe23a38ab477d..d98cb8e37cee1391212260c277df0f1d42364a65 100644 (file)
@@ -199,6 +199,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
       secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
       jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
       signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
+      lv_signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
       {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
       template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
 
@@ -217,6 +218,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
           secret: secret,
           jwt_secret: jwt_secret,
           signing_salt: signing_salt,
+          lv_signing_salt: lv_signing_salt,
           web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
           web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
           db_configurable?: db_configurable?,
index b88f74f4717ff27f24b12da153f6f65def14ed23..4106feef6d5baac44bc62c422ad4d3df2d6e2980 100644 (file)
@@ -362,11 +362,9 @@ defmodule Pleroma.Activity do
   end
 
   def restrict_deactivated_users(query) do
-    deactivated_users =
-      from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
-      |> Repo.all()
+    deactivated_users_query = from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
 
-    Activity.Queries.exclude_authors(query, deactivated_users)
+    from(activity in query, where: activity.actor not in subquery(deactivated_users_query))
   end
 
   defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
index 2a9addabcecff4553bff21c483e9db83c8beb270..0e3e1e5deaee1762268239d5615699b8fa7a6199 100644 (file)
@@ -9,7 +9,8 @@ defenum(Pleroma.UserRelationship.Type,
   mute: 2,
   reblog_mute: 3,
   notification_mute: 4,
-  inverse_subscription: 5
+  inverse_subscription: 5,
+  suggestion_dismiss: 6
 )
 
 defenum(Pleroma.FollowingRelationship.State,
index 95937a892bf567400a3d0a76a167b9c063b64fc9..abc95d902d1ce1486362ee89f69dfadf3d09a0a1 100644 (file)
@@ -103,6 +103,7 @@ defmodule Pleroma.Emoji.Loader do
     pack_file = Path.join(pack_dir, "pack.json")
 
     if File.exists?(pack_file) do
+      Logger.info("Loading emoji pack from JSON: #{pack_file}")
       contents = Jason.decode!(File.read!(pack_file))
 
       contents["files"]
@@ -115,6 +116,7 @@ defmodule Pleroma.Emoji.Loader do
       emoji_txt = Path.join(pack_dir, "emoji.txt")
 
       if File.exists?(emoji_txt) do
+        Logger.info("Loading emoji pack from emoji.txt: #{emoji_txt}")
         load_from_file(emoji_txt, emoji_groups)
       else
         extensions = Config.get([:emoji, :pack_extensions])
index 993cff09b26cf3c9c6f819a16c1ee74d2fab2c90..adb51d33a17436ad315d42c5ecec88c1238783ee 100644 (file)
@@ -338,6 +338,26 @@ defmodule Pleroma.ModerationLog do
     "@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
   end
 
+  def get_log_entry_message(%ModerationLog{
+        data: %{
+          "actor" => %{"nickname" => actor_nickname},
+          "action" => "add_suggestion",
+          "subject" => users
+        }
+      }) do
+    "@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}"
+  end
+
+  def get_log_entry_message(%ModerationLog{
+        data: %{
+          "actor" => %{"nickname" => actor_nickname},
+          "action" => "remove_suggestion",
+          "subject" => users
+        }
+      }) do
+    "@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}"
+  end
+
   def get_log_entry_message(%ModerationLog{
         data: %{
           "actor" => %{"nickname" => actor_nickname},
index 3b4e49176c65232edf07a67196f53875464c3457..c25023dc12b1c1b8b9b20e1b3b95fae17ec4aa28 100644 (file)
@@ -148,6 +148,7 @@ defmodule Pleroma.User do
     field(:last_active_at, :naive_datetime)
     field(:disclose_client, :boolean, default: true)
     field(:pinned_objects, :map, default: %{})
+    field(:is_suggested, :boolean, default: false)
 
     embeds_one(
       :notification_settings,
@@ -1676,6 +1677,22 @@ defmodule Pleroma.User do
 
   def confirm(%User{} = user), do: {:ok, user}
 
+  def set_suggestion(users, is_suggested) when is_list(users) do
+    Repo.transaction(fn ->
+      Enum.map(users, fn user ->
+        with {:ok, user} <- set_suggestion(user, is_suggested), do: user
+      end)
+    end)
+  end
+
+  def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
+
+  def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
+    user
+    |> change(is_suggested: is_suggested)
+    |> update_and_set_cache()
+  end
+
   def update_notification_settings(%User{} = user, settings) do
     user
     |> cast(%{notification_settings: settings}, [])
@@ -2474,8 +2491,8 @@ defmodule Pleroma.User do
     |> update_and_set_cache()
   end
 
-  def active_user_count(weeks \\ 4) do
-    active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks)
+  def active_user_count(days \\ 30) do
+    active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
 
     __MODULE__
     |> where([u], u.last_active_at >= ^active_after)
index ac807fc7927f5340cf37c9fbc83329b222e987c7..6d4a4ead6c480797ebcc2bed1efdf94389ed99fc 100644 (file)
@@ -46,6 +46,7 @@ defmodule Pleroma.User.Query do
             unconfirmed: boolean(),
             is_admin: boolean(),
             is_moderator: boolean(),
+            is_suggested: boolean(),
             super_users: boolean(),
             invisible: boolean(),
             internal: boolean(),
@@ -167,6 +168,10 @@ defmodule Pleroma.User.Query do
     where(query, [u], u.is_confirmed == false)
   end
 
+  defp compose_query({:is_suggested, bool}, query) do
+    where(query, [u], u.is_suggested == ^bool)
+  end
+
   defp compose_query({:followers, %User{id: id}}, query) do
     query
     |> where([u], u.id != ^id)
index 837787b9fcd3955583d1121823cc84e1ccb3c1b8..59fef42d6cc8c1b7b7046d623f95dfce75014b14 100644 (file)
@@ -68,12 +68,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
     end
   end
 
-  defp handle_href(href, mediaType) do
+  defp handle_href(href, mediaType, data) do
     [
       %{
         "href" => href,
         "type" => "Link",
-        "mediaType" => mediaType
+        "mediaType" => mediaType,
+        "width" => data["width"],
+        "height" => data["height"]
       }
     ]
   end
@@ -81,10 +83,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
   defp fix_url(data) do
     cond do
       is_binary(data["url"]) ->
-        Map.put(data, "url", handle_href(data["url"], data["mediaType"]))
+        Map.put(data, "url", handle_href(data["url"], data["mediaType"], data))
 
       is_binary(data["href"]) and data["url"] == nil ->
-        Map.put(data, "url", handle_href(data["href"], data["mediaType"]))
+        Map.put(data, "url", handle_href(data["href"], data["mediaType"], data))
 
       true ->
         data
index 4f29a441136a2377eb3a7f1cd6ea07430c708e86..ed99079e2f7c9dd0ffce56c70ee532024074f350 100644 (file)
@@ -63,18 +63,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
         date: date
       })
 
-    with {:ok, %{status: code}} when code in 200..299 <-
-           result =
-             HTTP.post(
-               inbox,
-               json,
-               [
-                 {"Content-Type", "application/activity+json"},
-                 {"Date", date},
-                 {"signature", signature},
-                 {"digest", digest}
-               ]
-             ) do
+    with {:ok, %{status: code}} = result when code in 200..299 <-
+           HTTP.post(
+             inbox,
+             json,
+             [
+               {"Content-Type", "application/activity+json"},
+               {"Date", date},
+               {"signature", signature},
+               {"digest", digest}
+             ]
+           ) do
       if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do
         Instances.set_reachable(inbox)
       end
index 701181a1419e2f12bce3932fba3d0014832019da..d55a4b340c9a189f193928add8076bbe3c3dda65 100644 (file)
@@ -200,7 +200,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
       {:ok, notifications} = Notification.create_notifications(activity, do_send: false)
       {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
 
-      if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
+      if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do
         Object.increase_replies_count(in_reply_to)
       end
 
index 1df53f79ad75f20dc1f8b7ef193e7c52d1d8b9a9..c1f6b2b49838d6bbcf6c07f0002f496e553ce397 100644 (file)
@@ -446,7 +446,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
     |> Activity.Queries.by_type()
     |> Activity.Queries.by_actor(actor)
     |> Activity.Queries.by_object_id(object)
-    |> where(fragment("data->>'state' = 'pending'"))
+    |> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'"))
     |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
     |> Repo.update_all([])
 
index 637a0e702d37003800144b4767d91de8a7a99d22..50208a8b76a1b4ac634632a29d69f58f0326c80f 100644 (file)
@@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do
            :toggle_activation,
            :activate,
            :deactivate,
-           :approve
+           :approve,
+           :suggest,
+           :unsuggest
          ]
   )
 
@@ -239,6 +241,32 @@ defmodule Pleroma.Web.AdminAPI.UserController do
     render(conn, "index.json", users: updated_users)
   end
 
+  def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.set_suggestion(users, true)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "add_suggestion"
+    })
+
+    render(conn, "index.json", users: updated_users)
+  end
+
+  def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.set_suggestion(users, false)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "remove_suggestion"
+    })
+
+    render(conn, "index.json", users: updated_users)
+  end
+
   def index(conn, params) do
     {page, page_size} = page_params(params)
     filters = maybe_parse_filters(params[:filters])
index fae0c07f009dfca23af1779d5ca0227179548502..2f1f7e627e7b078a9f5b8e1478398ec88ba7e9d2 100644 (file)
@@ -80,6 +80,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
       "tags" => user.tags || [],
       "is_confirmed" => user.is_confirmed,
       "is_approved" => user.is_approved,
+      "is_suggested" => user.is_suggested,
       "url" => user.uri || user.ap_id,
       "registration_reason" => user.registration_reason,
       "actor_type" => user.actor_type,
index 8613f3a98729faab595d45eb4d786492d2ba3483..8cd2e824d1e47d07eb5c1c24cefc7a869c043cc3 100644 (file)
@@ -226,6 +226,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
                 type: :boolean,
                 description: "Receive this account's reblogs in home timeline? Defaults to true.",
                 default: true
+              },
+              notify: %Schema{
+                type: :boolean,
+                description:
+                  "Receive notifications for all statuses posted by the account? Defaults to false.",
+                default: false
               }
             }
           },
@@ -712,6 +718,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
           "requested" => false,
           "domain_blocking" => false,
           "subscribing" => false,
+          "notifying" => false,
           "endorsed" => true
         },
         %{
@@ -727,6 +734,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
           "requested" => true,
           "domain_blocking" => false,
           "subscribing" => false,
+          "notifying" => false,
           "endorsed" => false
         },
         %{
@@ -742,6 +750,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
           "requested" => false,
           "domain_blocking" => true,
           "subscribing" => true,
+          "notifying" => true,
           "endorsed" => false
         }
       ]
index c9d0bfd7c1d0f00d6ad42803c093fe7b0f8867a7..57fb1ad65e8c101af4700cfbafd0017a1c6efaab 100644 (file)
@@ -216,7 +216,71 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do
         request_body(
           "Parameters",
           %Schema{
-            description: "POST body for deleting multiple users",
+            description: "POST body for approving multiple users",
+            type: :object,
+            properties: %{
+              nicknames: %Schema{
+                type: :array,
+                items: %Schema{type: :string}
+              }
+            }
+          }
+        ),
+      responses: %{
+        200 =>
+          Operation.response("Response", "application/json", %Schema{
+            type: :object,
+            properties: %{user: %Schema{type: :array, items: user()}}
+          }),
+        403 => Operation.response("Forbidden", "application/json", ApiError)
+      }
+    }
+  end
+
+  def suggest_operation do
+    %Operation{
+      tags: ["User administration"],
+      summary: "Suggest multiple users",
+      operationId: "AdminAPI.UserController.suggest",
+      security: [%{"oAuth" => ["admin:write:accounts"]}],
+      parameters: admin_api_params(),
+      requestBody:
+        request_body(
+          "Parameters",
+          %Schema{
+            description: "POST body for adding multiple suggested users",
+            type: :object,
+            properties: %{
+              nicknames: %Schema{
+                type: :array,
+                items: %Schema{type: :string}
+              }
+            }
+          }
+        ),
+      responses: %{
+        200 =>
+          Operation.response("Response", "application/json", %Schema{
+            type: :object,
+            properties: %{user: %Schema{type: :array, items: user()}}
+          }),
+        403 => Operation.response("Forbidden", "application/json", ApiError)
+      }
+    }
+  end
+
+  def unsuggest_operation do
+    %Operation{
+      tags: ["User administration"],
+      summary: "Unsuggest multiple users",
+      operationId: "AdminAPI.UserController.unsuggest",
+      security: [%{"oAuth" => ["admin:write:accounts"]}],
+      parameters: admin_api_params(),
+      requestBody:
+        request_body(
+          "Parameters",
+          %Schema{
+            description: "POST body for removing multiple suggested users",
             type: :object,
             properties: %{
               nicknames: %Schema{
index ebcfd3be231e661871a1409147331bf8fd228875..2a701066d409549e5fcdde77fcc04a0558cc7db9 100644 (file)
@@ -191,6 +191,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
       parameters: [
         Operation.parameter(:password, :query, :string, "Password")
       ],
+      requestBody: request_body("Parameters", delete_account_request(), required: false),
       responses: %{
         200 =>
           Operation.response("Success", "application/json", %Schema{
@@ -237,4 +238,48 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
       responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
     }
   end
+
+  def remote_interaction_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Remote interaction",
+      operationId: "UtilController.remote_interaction",
+      requestBody: request_body("Parameters", remote_interaction_request(), required: true),
+      responses: %{
+        200 =>
+          Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
+      }
+    }
+  end
+
+  defp remote_interaction_request do
+    %Schema{
+      title: "RemoteInteractionRequest",
+      description: "POST body for remote interaction",
+      type: :object,
+      required: [:ap_id, :profile],
+      properties: %{
+        ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
+        profile: %Schema{type: :string, description: "Remote profile webfinger"}
+      }
+    }
+  end
+
+  defp delete_account_request do
+    %Schema{
+      title: "AccountDeleteRequest",
+      description: "POST body for deleting one's own account",
+      type: :object,
+      properties: %{
+        password: %Schema{
+          type: :string,
+          description: "The user's own password for confirmation.",
+          format: :password
+        }
+      },
+      example: %{
+        "password" => "prettyp0ony1313"
+      }
+    }
+  end
 end
index e0bd2728b8731864696aca6cec008a2cea885468..548e7054412f32a3874ef7bce139828bed0593ee 100644 (file)
@@ -197,7 +197,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
           "note" => "",
           "requested" => false,
           "showing_reblogs" => true,
-          "subscribing" => false
+          "subscribing" => false,
+          "notifying" => false
         },
         "settings_store" => %{
           "pleroma-fe" => %{}
index 163066032a9154d690b977dfe84ce06cfac48433..5d9e3b56e3310f2893fb784cc2921d9c4aacfdce 100644 (file)
@@ -25,7 +25,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
       note: %Schema{type: :string},
       requested: %Schema{type: :boolean},
       showing_reblogs: %Schema{type: :boolean},
-      subscribing: %Schema{type: :boolean}
+      subscribing: %Schema{type: :boolean},
+      notifying: %Schema{type: :boolean}
     },
     example: %{
       "blocked_by" => false,
@@ -40,7 +41,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
       "note" => "",
       "requested" => false,
       "showing_reblogs" => true,
-      "subscribing" => false
+      "subscribing" => false,
+      "notifying" => false
     }
   })
 end
index 60801f3227e1f103b1441b61d8d695b115eb5bac..3caab0f00dd7f723144e73d89d092569b30a115b 100644 (file)
@@ -285,7 +285,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
             "note" => "",
             "requested" => false,
             "showing_reblogs" => true,
-            "subscribing" => false
+            "subscribing" => false,
+            "notifying" => false
           },
           "skip_thread_containment" => false,
           "tags" => []
index 8e274de889522ffd1771b6e84fe4595bd69f7783..75484fac5ee768c401082a4f9ab5e3183ab3a589 100644 (file)
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.Endpoint do
   alias Pleroma.Config
 
   socket("/socket", Pleroma.Web.UserSocket)
+  socket("/live", Phoenix.LiveView.Socket)
 
   plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
 
diff --git a/lib/pleroma/web/manifest_controller.ex b/lib/pleroma/web/manifest_controller.ex
new file mode 100644 (file)
index 0000000..5258954
--- /dev/null
@@ -0,0 +1,14 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ManifestController do
+  use Pleroma.Web, :controller
+
+  plug(:skip_auth when action == :show)
+
+  @doc "GET /manifest.json"
+  def show(conn, _params) do
+    render(conn, "manifest.json")
+  end
+end
index 64b177eb3b11439e143fb4f40e1170c6bfa0d6f8..1459fc49233962e023454d3adfb508741292355a 100644 (file)
@@ -17,6 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
 
   require Logger
 
+  @search_limit 40
+
   plug(Pleroma.Web.ApiSpec.CastAndValidate)
 
   # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
@@ -77,7 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
     [
       resolve: params[:resolve],
       following: params[:following],
-      limit: params[:limit],
+      limit: min(params[:limit], @search_limit),
       offset: params[:offset],
       type: params[:type],
       author: get_author(params),
index 01e122dd9020dfbbae6ed9d3ec05cadd1237bc83..e913fcf4bbb5c66f8e850ca81fb832e57e3f9bd6 100644 (file)
@@ -4,11 +4,16 @@
 
 defmodule Pleroma.Web.MastodonAPI.SuggestionController do
   use Pleroma.Web, :controller
+  import Ecto.Query
+  alias Pleroma.FollowingRelationship
+  alias Pleroma.User
+  alias Pleroma.UserRelationship
 
   require Logger
 
   plug(Pleroma.Web.ApiSpec.CastAndValidate)
-  plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
+  plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2])
+  plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss])
 
   def open_api_operation(action) do
     operation = String.to_existing_atom("#{action}_operation")
@@ -26,7 +31,90 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do
     }
   end
 
+  def index2_operation do
+    %OpenApiSpex.Operation{
+      tags: ["Suggestions"],
+      summary: "Follow suggestions",
+      operationId: "SuggestionController.index2",
+      responses: %{
+        200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response()
+      }
+    }
+  end
+
+  def dismiss_operation do
+    %OpenApiSpex.Operation{
+      tags: ["Suggestions"],
+      summary: "Remove a suggestion",
+      operationId: "SuggestionController.dismiss",
+      parameters: [
+        OpenApiSpex.Operation.parameter(
+          :account_id,
+          :path,
+          %OpenApiSpex.Schema{type: :string},
+          "Account to dismiss",
+          required: true
+        )
+      ],
+      responses: %{
+        200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response()
+      }
+    }
+  end
+
   @doc "GET /api/v1/suggestions"
   def index(conn, params),
     do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
+
+  @doc "GET /api/v2/suggestions"
+  def index2(%{assigns: %{user: user}} = conn, params) do
+    limit = Map.get(params, :limit, 40) |> min(80)
+
+    users =
+      %{is_suggested: true, invisible: false, limit: limit}
+      |> User.Query.build()
+      |> exclude_user(user)
+      |> exclude_relationships(user, [:block, :mute, :suggestion_dismiss])
+      |> exclude_following(user)
+      |> Pleroma.Repo.all()
+
+    render(conn, "index.json", %{
+      users: users,
+      source: :staff,
+      for: user,
+      skip_visibility_check: true
+    })
+  end
+
+  defp exclude_user(query, %User{id: user_id}) do
+    where(query, [u], u.id != ^user_id)
+  end
+
+  defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
+    query
+    |> join(:left, [u], r in UserRelationship,
+      as: :user_relationships,
+      on:
+        r.target_id == u.id and r.source_id == ^user_id and
+          r.relationship_type in ^relationship_types
+    )
+    |> where([user_relationships: r], is_nil(r.target_id))
+  end
+
+  defp exclude_following(query, %User{id: user_id}) do
+    query
+    |> join(:left, [u], r in FollowingRelationship,
+      as: :following_relationships,
+      on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept
+    )
+    |> where([following_relationships: r], is_nil(r.following_id))
+  end
+
+  @doc "DELETE /api/v1/suggestions/:account_id"
+  def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do
+    with %User{} = target <- User.get_cached_by_id(user_id),
+         {:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do
+      json(conn, %{})
+    end
+  end
 end
index 71479550eb61402617bc97d3dd560502135dd4a7..23846b36a0a3101784b2e27358b5de14407c26c3 100644 (file)
@@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
     with {:ok, follower, _followed, _} <- result do
       options = cast_params(params)
       set_reblogs_visibility(options[:reblogs], result)
+      set_subscription(options[:notify], result)
       {:ok, follower}
     end
   end
@@ -36,6 +37,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
     CommonAPI.show_reblogs(follower, followed)
   end
 
+  defp set_subscription(true, {:ok, follower, followed, _}) do
+    User.subscribe(follower, followed)
+  end
+
+  defp set_subscription(false, {:ok, follower, followed, _}) do
+    User.unsubscribe(follower, followed)
+  end
+
+  defp set_subscription(_, _), do: {:ok, nil}
+
   @spec get_followers(User.t(), map()) :: list(User.t())
   def get_followers(user, params \\ %{}) do
     user
@@ -73,7 +84,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
       exclude_visibilities: {:array, :string},
       reblogs: :boolean,
       with_muted: :boolean,
-      account_ap_id: :string
+      account_ap_id: :string,
+      notify: :boolean
     }
 
     changeset = cast({%{}, param_types}, params, Map.keys(param_types))
index a3a9f95487a00e56e6087dd05df91528ace7513c..3c8dd03537b5d79860ffec4a0c9e58e733b43700 100644 (file)
@@ -102,6 +102,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         User.following?(target, reading_user)
       end
 
+    subscribing =
+      UserRelationship.exists?(
+        user_relationships,
+        :inverse_subscription,
+        target,
+        reading_user,
+        &User.subscribed_to?(&2, &1)
+      )
+
     # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
     %{
       id: to_string(target.id),
@@ -139,14 +148,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
           target,
           &User.muted_notifications?(&1, &2)
         ),
-      subscribing:
-        UserRelationship.exists?(
-          user_relationships,
-          :inverse_subscription,
-          target,
-          reading_user,
-          &User.subscribed_to?(&2, &1)
-        ),
+      subscribing: subscribing,
+      notifying: subscribing,
       requested: follow_state == :follow_pending,
       domain_blocking: User.blocks_domain?(reading_user, target),
       showing_reblogs:
@@ -275,6 +278,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         ap_id: user.ap_id,
         also_known_as: user.also_known_as,
         is_confirmed: user.is_confirmed,
+        is_suggested: user.is_suggested,
         tags: user.tags,
         hide_followers_count: user.hide_followers_count,
         hide_follows_count: user.hide_follows_count,
index ef208062bdb121f12ecd77be6c5d5d38979d4587..ec7d150a97848603303d16ecee3950bd1d3c22ac 100644 (file)
@@ -59,6 +59,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
       "mastodon_api",
       "mastodon_api_streaming",
       "polls",
+      "v2_suggestions",
       "pleroma_explicit_addressing",
       "shareable_emoji_packs",
       "multifetch",
@@ -83,7 +84,10 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
         "safe_dm_mentions"
       end,
       "pleroma_emoji_reactions",
-      "pleroma_chat_messages"
+      "pleroma_chat_messages",
+      if Config.get([:instance, :show_reactions]) do
+        "exposable_reactions"
+      end
     ]
     |> Enum.filter(& &1)
   end
diff --git a/lib/pleroma/web/mastodon_api/views/suggestion_view.ex b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex
new file mode 100644 (file)
index 0000000..865229a
--- /dev/null
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SuggestionView do
+  use Pleroma.Web, :view
+  alias Pleroma.Web.MastodonAPI.AccountView
+
+  @source_types [:staff, :global, :past_interactions]
+
+  def render("index.json", %{users: users} = opts) do
+    Enum.map(users, fn user ->
+      opts =
+        opts
+        |> Map.put(:user, user)
+        |> Map.delete(:users)
+
+      render("show.json", opts)
+    end)
+  end
+
+  def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do
+    %{
+      source: source,
+      account: AccountView.render("show.json", opts)
+    }
+  end
+end
index 6a0112d2a2c1304874f8b54c2155dfd4a7c5a0b1..3781781c849ae3ad8618bd0c8ddc00edd2fb5f8d 100644 (file)
@@ -35,7 +35,9 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
       openRegistrations: Config.get([:instance, :registrations_open]),
       usage: %{
         users: %{
-          total: Map.get(stats, :user_count, 0)
+          total: Map.get(stats, :user_count, 0),
+          activeMonth: Pleroma.User.active_user_count(30),
+          activeHalfyear: Pleroma.User.active_user_count(180)
         },
         localPosts: Map.get(stats, :status_count, 0)
       },
index dcd54b1afccdcadad2ab1755e0831cae57721af9..669d50132c584e34760c2fb9712b9d64df83f67f 100644 (file)
@@ -151,7 +151,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
       index_query(user, params)
       |> Pagination.fetch_paginated(params)
 
-    render(conn, "index.json", chats: chats)
+    conn
+    |> add_link_headers(chats)
+    |> render("index.json", chats: chats)
   end
 
   defp index_query(%{id: user_id} = user, params) do
index ca5db8ea359bf1a7433abea01c2e320540d1286c..9ce35ad6bd597d68ead6fd7af1bc72b4b139bfbc 100644 (file)
@@ -4,6 +4,7 @@
 
 defmodule Pleroma.Web.Router do
   use Pleroma.Web, :router
+  import Phoenix.LiveDashboard.Router
 
   pipeline :accepts_html do
     plug(:accepts, ["html"])
@@ -150,6 +151,7 @@ defmodule Pleroma.Web.Router do
     get("/emoji", UtilController, :emoji)
     get("/captcha", UtilController, :captcha)
     get("/healthcheck", UtilController, :healthcheck)
+    post("/remote_interaction", UtilController, :remote_interaction)
   end
 
   scope "/api/v1/pleroma", Pleroma.Web do
@@ -157,12 +159,11 @@ defmodule Pleroma.Web.Router do
     post("/uploader_callback/:upload_path", UploaderController, :callback)
   end
 
+  # AdminAPI: only admins can perform these actions
   scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
     pipe_through([:admin_api, :require_admin])
 
     put("/users/disable_mfa", AdminAPIController, :disable_mfa)
-    put("/users/tag", AdminAPIController, :tag_users)
-    delete("/users/tag", AdminAPIController, :untag_users)
 
     get("/users/:nickname/permission_group", AdminAPIController, :right_get)
     get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
@@ -185,16 +186,61 @@ defmodule Pleroma.Web.Router do
 
     post("/users/follow", UserController, :follow)
     post("/users/unfollow", UserController, :unfollow)
-    delete("/users", UserController, :delete)
     post("/users", UserController, :create)
+
+    patch("/users/suggest", UserController, :suggest)
+    patch("/users/unsuggest", UserController, :unsuggest)
+
+    get("/relay", RelayController, :index)
+    post("/relay", RelayController, :follow)
+    delete("/relay", RelayController, :unfollow)
+
+    get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
+    patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
+    get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
+    patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
+
+    get("/instance_document/:name", InstanceDocumentController, :show)
+    patch("/instance_document/:name", InstanceDocumentController, :update)
+    delete("/instance_document/:name", InstanceDocumentController, :delete)
+
+    patch("/users/confirm_email", AdminAPIController, :confirm_email)
+    patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
+
+    get("/config", ConfigController, :show)
+    post("/config", ConfigController, :update)
+    get("/config/descriptions", ConfigController, :descriptions)
+    get("/need_reboot", AdminAPIController, :need_reboot)
+    get("/restart", AdminAPIController, :restart)
+
+    get("/oauth_app", OAuthAppController, :index)
+    post("/oauth_app", OAuthAppController, :create)
+    patch("/oauth_app/:id", OAuthAppController, :update)
+    delete("/oauth_app/:id", OAuthAppController, :delete)
+
+    get("/media_proxy_caches", MediaProxyCacheController, :index)
+    post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
+    post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
+
+    get("/frontends", FrontendController, :index)
+    post("/frontends/install", FrontendController, :install)
+
+    post("/backups", AdminAPIController, :create_backup)
+  end
+
+  # AdminAPI: admins and mods (staff) can perform these actions
+  scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+    pipe_through(:admin_api)
+
+    put("/users/tag", AdminAPIController, :tag_users)
+    delete("/users/tag", AdminAPIController, :untag_users)
+
     patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
     patch("/users/activate", UserController, :activate)
     patch("/users/deactivate", UserController, :deactivate)
     patch("/users/approve", UserController, :approve)
 
-    get("/relay", RelayController, :index)
-    post("/relay", RelayController, :follow)
-    delete("/relay", RelayController, :unfollow)
+    delete("/users", UserController, :delete)
 
     post("/users/invite_token", InviteController, :create)
     get("/users/invites", InviteController, :index)
@@ -214,13 +260,6 @@ defmodule Pleroma.Web.Router do
     get("/instances/:instance/statuses", InstanceController, :list_statuses)
     delete("/instances/:instance", InstanceController, :delete)
 
-    get("/instance_document/:name", InstanceDocumentController, :show)
-    patch("/instance_document/:name", InstanceDocumentController, :update)
-    delete("/instance_document/:name", InstanceDocumentController, :delete)
-
-    patch("/users/confirm_email", AdminAPIController, :confirm_email)
-    patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
-
     get("/reports", ReportController, :index)
     get("/reports/:id", ReportController, :show)
     patch("/reports", ReportController, :update)
@@ -232,39 +271,19 @@ defmodule Pleroma.Web.Router do
     delete("/statuses/:id", StatusController, :delete)
     get("/statuses", StatusController, :index)
 
-    get("/config", ConfigController, :show)
-    post("/config", ConfigController, :update)
-    get("/config/descriptions", ConfigController, :descriptions)
-    get("/need_reboot", AdminAPIController, :need_reboot)
-    get("/restart", AdminAPIController, :restart)
-
     get("/moderation_log", AdminAPIController, :list_log)
 
     post("/reload_emoji", AdminAPIController, :reload_emoji)
     get("/stats", AdminAPIController, :stats)
 
-    get("/oauth_app", OAuthAppController, :index)
-    post("/oauth_app", OAuthAppController, :create)
-    patch("/oauth_app/:id", OAuthAppController, :update)
-    delete("/oauth_app/:id", OAuthAppController, :delete)
-
-    get("/media_proxy_caches", MediaProxyCacheController, :index)
-    post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
-    post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
-
     get("/chats/:id", ChatController, :show)
     get("/chats/:id/messages", ChatController, :messages)
     delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
-
-    get("/frontends", FrontendController, :index)
-    post("/frontends/install", FrontendController, :install)
-
-    post("/backups", AdminAPIController, :create_backup)
   end
 
   scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
     scope "/pack" do
-      pipe_through([:admin_api, :require_admin])
+      pipe_through(:admin_api)
 
       post("/", EmojiPackController, :create)
       patch("/", EmojiPackController, :update)
@@ -279,7 +298,7 @@ defmodule Pleroma.Web.Router do
 
     # Modifying packs
     scope "/packs" do
-      pipe_through([:admin_api, :require_admin])
+      pipe_through(:admin_api)
 
       get("/import", EmojiPackController, :import_from_filesystem)
       get("/remote", EmojiPackController, :remote)
@@ -536,6 +555,7 @@ defmodule Pleroma.Web.Router do
     delete("/push/subscription", SubscriptionController, :delete)
 
     get("/suggestions", SuggestionController, :index)
+    delete("/suggestions/:account_id", SuggestionController, :dismiss)
 
     get("/timelines/home", TimelineController, :home)
     get("/timelines/direct", TimelineController, :direct)
@@ -587,6 +607,8 @@ defmodule Pleroma.Web.Router do
     get("/search", SearchController, :search2)
 
     post("/media", MediaController, :create2)
+
+    get("/suggestions", SuggestionController, :index2)
   end
 
   scope "/api", Pleroma.Web do
@@ -737,6 +759,18 @@ defmodule Pleroma.Web.Router do
     get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
   end
 
+  scope "/", Pleroma.Web do
+    pipe_through(:api)
+
+    get("/manifest.json", ManifestController, :show)
+  end
+
+  scope "/", Pleroma.Web do
+    pipe_through(:pleroma_html)
+
+    post("/auth/password", TwitterAPI.PasswordController, :request)
+  end
+
   scope "/proxy/", Pleroma.Web do
     get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
     get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
@@ -752,6 +786,11 @@ defmodule Pleroma.Web.Router do
     end
   end
 
+  scope "/" do
+    pipe_through([:pleroma_html, :authenticate, :require_admin])
+    live_dashboard("/phoenix/live_dashboard")
+  end
+
   # Test-only routes needed to test action dispatching and plug chain execution
   if Pleroma.Config.get(:env) == :test do
     @test_actions [
index bc04a4d4910d4690d76a7af12fa49bed1c59dc01..133a588b018b1d85d2f283da2e49df15e1359998 100644 (file)
@@ -11,9 +11,23 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
 
   require Logger
 
+  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+
   alias Pleroma.PasswordResetToken
   alias Pleroma.Repo
   alias Pleroma.User
+  alias Pleroma.Web.TwitterAPI.TwitterAPI
+
+  plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
+
+  @doc "POST /auth/password"
+  def request(conn, params) do
+    nickname_or_email = params["email"] || params["nickname"]
+
+    TwitterAPI.password_reset(nickname_or_email)
+
+    json_response(conn, :no_content, "")
+  end
 
   def reset(conn, %{"token" => token}) do
     with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
index ef43f76820637cb8b44da7d423dc3ae288f4f4ed..ccbef6d9f11a85c80b04b9d180c334f526a01d6b 100644 (file)
@@ -62,6 +62,15 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
     end
   end
 
+  def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do
+    with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
+      conn
+      |> json(%{url: String.replace(template, "{uri}", ap_id)})
+    else
+      _e -> json(conn, %{error: "Couldn't find user"})
+    end
+  end
+
   def frontend_configurations(conn, _params) do
     render(conn, "frontend_configurations.json")
   end
@@ -123,8 +132,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
     end
   end
 
-  def delete_account(%{assigns: %{user: user}} = conn, params) do
-    password = params[:password] || ""
+  def delete_account(%{assigns: %{user: user}, body_params: body_params} = conn, params) do
+    # This endpoint can accept a query param or JSON body for backwards-compatibility.
+    # Submitting a JSON body is recommended, so passwords don't end up in server logs.
+    password = body_params[:password] || params[:password] || ""
 
     case CommonAPI.Utils.confirm_current_password(user, password) do
       {:ok, user} ->
diff --git a/lib/pleroma/web/views/manifest_view.ex b/lib/pleroma/web/views/manifest_view.ex
new file mode 100644 (file)
index 0000000..cc78ea3
--- /dev/null
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ManifestView do
+  use Pleroma.Web, :view
+  alias Pleroma.Config
+  alias Pleroma.Web.Endpoint
+
+  def render("manifest.json", _params) do
+    %{
+      name: Config.get([:instance, :name]),
+      description: Config.get([:instance, :description]),
+      icons: Config.get([:manifest, :icons]),
+      theme_color: Config.get([:manifest, :theme_color]),
+      background_color: Config.get([:manifest, :background_color]),
+      display: "standalone",
+      scope: Endpoint.url(),
+      start_url: "/",
+      categories: [
+        "social"
+      ],
+      serviceworker: %{
+        src: "/sw.js"
+      }
+    }
+  end
+end
diff --git a/mix.exs b/mix.exs
index 4ec76a50f94476f36794f4b893191ca881909fcc..615f1d50b87ae4f8876b9986b7791347cf844ae0 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -8,7 +8,7 @@ defmodule Pleroma.Mixfile do
       elixir: "~> 1.9",
       elixirc_paths: elixirc_paths(Mix.env()),
       compilers: [:phoenix, :gettext] ++ Mix.compilers(),
-      elixirc_options: [warnings_as_errors: warnings_as_errors(Mix.env())],
+      elixirc_options: [warnings_as_errors: warnings_as_errors()],
       xref: [exclude: [:eldap]],
       start_permanent: Mix.env() == :prod,
       aliases: aliases(),
@@ -79,6 +79,7 @@ defmodule Pleroma.Mixfile do
         :comeonin,
         :quack,
         :fast_sanitize,
+        :os_mon,
         :ssl
       ],
       included_applications: [:ex_syslogger]
@@ -86,12 +87,11 @@ defmodule Pleroma.Mixfile do
   end
 
   # Specifies which paths to compile per environment.
-  defp elixirc_paths(:benchmark), do: ["lib", "benchmarks"]
+  defp elixirc_paths(:benchmark), do: ["lib", "benchmarks", "priv/scrubbers"]
   defp elixirc_paths(:test), do: ["lib", "test/support"]
   defp elixirc_paths(_), do: ["lib"]
 
-  defp warnings_as_errors(:prod), do: false
-  defp warnings_as_errors(_), do: true
+  defp warnings_as_errors, do: System.get_env("CI") == "true"
 
   # Specifies OAuth dependencies.
   defp oauth_deps do
@@ -129,7 +129,7 @@ defmodule Pleroma.Mixfile do
       {:trailing_format_plug, "~> 0.0.7"},
       {:fast_sanitize, "~> 0.2.0"},
       {:html_entities, "~> 0.5", override: true},
-      {:phoenix_html, "~> 2.14"},
+      {:phoenix_html, "~> 3.1", override: true},
       {:calendar, "~> 1.0"},
       {:cachex, "~> 3.2"},
       {:poison, "~> 3.0", override: true},
@@ -197,6 +197,8 @@ defmodule Pleroma.Mixfile do
        ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"},
       {:eblurhash, "~> 1.1.0"},
       {:open_api_spex, "~> 3.10"},
+      {:phoenix_live_dashboard, "~> 0.6.2"},
+      {:ecto_psql_extras, "~> 0.6"},
 
       # indirect dependency version override
       {:plug, "~> 1.10.4", override: true},
@@ -208,7 +210,7 @@ defmodule Pleroma.Mixfile do
       {:mock, "~> 0.3.5", only: :test},
       # temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed
       {:excoveralls, "0.12.3", only: :test},
-      {:hackney, "~> 1.17.0", override: true},
+      {:hackney, "~> 1.18.0", override: true},
       {:mox, "~> 1.0", only: :test},
       {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
     ] ++ oauth_deps()
index f29fdef62c1879695c8dd87dde3de032bea8c3d3..5b95272046634fe8ff4e36d2ca21dd6caead05c4 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -11,7 +11,7 @@
   "calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
   "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
   "castore": {:hex, :castore, "0.1.10", "b01a007416a0ae4188e70b3b306236021b16c11474038ead7aff79dd75538c23", [:mix], [], "hexpm", "a48314e0cb45682db2ea27b8ebfa11bd6fa0a6e21a65e5772ad83ca136ff2665"},
-  "certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"},
+  "certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
   "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
   "comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
   "concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]},
@@ -32,6 +32,7 @@
   "eblurhash": {:hex, :eblurhash, "1.1.0", "e10ccae762598507ebfacf0b645ed49520f2afa3e7e9943e73a91117dffce415", [:rebar3], [], "hexpm", "2e6b889d09fddd374e3c5ac57c486138768763264e99ac1074ae5fa7fc9ab51d"},
   "ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
   "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
+  "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.4", "5d43fd088d39a158c860b17e8d210669587f63ec89ea122a4654861c8c6e2db4", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.15.7", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "311db02f1b772e3d0dc7f56a05044b5e1499d78ed6abf38885e1ca70059449e5"},
   "ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
   "eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
   "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
@@ -55,7 +56,7 @@
   "gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
   "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
   "gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
-  "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
+  "hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
   "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
   "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
   "http_signatures": {:hex, :http_signatures, "0.1.1", "ca7ebc1b61542b163644c8c3b1f0e0f41037d35f2395940d3c6c7deceab41fd8", [:mix], [], "hexpm", "cc3b8a007322cc7b624c0c15eec49ee58ac977254ff529a3c482f681465942a3"},
@@ -91,7 +92,9 @@
   "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
   "phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},
   "phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
-  "phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
+  "phoenix_html": {:hex, :phoenix_html, "3.1.0", "0b499df05aad27160d697a9362f0e89fa0e24d3c7a9065c2bd9d38b4d1416c09", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0c0a98a2cefa63433657983a2a594c7dee5927e4391e0f1bfd3a151d1def33fc"},
+  "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.2", "0769470265eb13af01b5001b29cb935f4710d6adaa1ffc18417a570a337a2f0f", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5bc6c6b38a2ca8b5020b442322fcee6afd5e641637a0b1fb059d4bd89bc58e7b"},
+  "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.5", "63f52a6f9f6983f04e424586ff897c016ecc5e4f8d1e2c22c2887af1c57215d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c5586e6a3d4df71b8214c769d4f5eb8ece2b4001711a7ca0f97323c36958b0e3"},
   "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
   "phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.3", "039435dd975f7e55953525b88f1d596f26c6141412584c16f4db109708a8ee68", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4a540cea32e05356541737033d666ee7fea7700eb2101bf76783adbfe06601cd"},
   "plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"},
   "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
   "swoosh": {:hex, :swoosh, "1.3.11", "34f79c57f19892b43bd2168de9ff5de478a721a26328ef59567aad4243e7a77b", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f1e2a048db454f9982b9cf840f75e7399dd48be31ecc2a7dc10012a803b913af"},
   "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
+  "table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"},
   "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"},
+  "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
   "tesla": {:hex, :tesla, "1.4.1", "ff855f1cac121e0d16281b49e8f066c4a0d89965f98864515713878cca849ac8", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "95f5de35922c8c4b3945bee7406f66eb680b0955232f78f5fb7e853aa1ce201a"},
   "timex": {:hex, :timex, "3.7.5", "3eca56e23bfa4e0848f0b0a29a92fa20af251a975116c6d504966e8a90516dfd", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "a15608dca680f2ef663d71c95842c67f0af08a0f3b1d00e17bbd22872e2874e4"},
   "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
diff --git a/priv/repo/migrations/20211125110126_force_pinned_objects_to_exist.exs b/priv/repo/migrations/20211125110126_force_pinned_objects_to_exist.exs
new file mode 100644 (file)
index 0000000..1fe9271
--- /dev/null
@@ -0,0 +1,11 @@
+defmodule Pleroma.Repo.Migrations.ForcePinnedObjectsToExist do
+  use Ecto.Migration
+
+  def change do
+    execute("UPDATE users SET pinned_objects = '{}' WHERE pinned_objects IS NULL")
+
+    alter table("users") do
+      modify(:pinned_objects, :map, null: false, default: %{})
+    end
+  end
+end
diff --git a/priv/repo/migrations/20211126191138_add_suggestions.exs b/priv/repo/migrations/20211126191138_add_suggestions.exs
new file mode 100644 (file)
index 0000000..7cc67d8
--- /dev/null
@@ -0,0 +1,11 @@
+defmodule Pleroma.Repo.Migrations.AddSuggestions do
+  use Ecto.Migration
+
+  def change do
+    alter table(:users) do
+      add(:is_suggested, :boolean, default: false, null: false)
+    end
+
+    create_if_not_exists(index(:users, [:is_suggested]))
+  end
+end
index 42f496ded4611dc8f0ceaf4e55f32232706bee09..0068969ac4b573d3b69c7ec0433249936ac86fcc 100644 (file)
@@ -13,6 +13,7 @@ config :pleroma, Pleroma.Web.Endpoint,
    url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
    http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>],
    secret_key_base: "<%= secret %>",
+   live_view: [signing_salt: "<%= lv_signing_salt %>"],
    signing_salt: "<%= signing_salt %>"
 
 config :pleroma, :instance,
index 7d51fd84ce50fa4a3f0f6cac0a4480d9b96ed753..9e3f11f1a8741e4e09fc919c0f54d8dabbed0bf6 100644 (file)
@@ -82,6 +82,7 @@ defmodule Pleroma.Config.TransferTaskTest do
       on_exit(fn -> Restarter.Pleroma.refresh() end)
     end
 
+    @tag :erratic
     test "don't restart if no reboot time settings were changed" do
       clear_config(:emoji)
       insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]])
@@ -92,18 +93,21 @@ defmodule Pleroma.Config.TransferTaskTest do
              )
     end
 
+    @tag :erratic
     test "on reboot time key" do
       clear_config(:shout)
       insert(:config, key: :shout, value: [enabled: false])
       assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
     end
 
+    @tag :erratic
     test "on reboot time subkey" do
       clear_config(Pleroma.Captcha)
       insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
       assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
     end
 
+    @tag :erratic
     test "don't restart pleroma on reboot time key and subkey if there is false flag" do
       clear_config(:shout)
       clear_config(Pleroma.Captcha)
index 357016e3e2ce9874876c3a792ce076f00fdf9c64..363da7665967546d97489a42b8a3109f1d3281fd 100644 (file)
@@ -34,4 +34,14 @@ defmodule Pleroma.User.QueryTest do
       assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2
     end
   end
+
+  test "is_suggested param" do
+    _user1 = insert(:user, is_suggested: false)
+    user2 = insert(:user, is_suggested: true)
+
+    assert [^user2] =
+             %{is_suggested: true}
+             |> User.Query.build()
+             |> Repo.all()
+  end
 end
index 5fef81245b0af61959f4060cf2c124ab4ab70eb0..6cd93c34c28d598a7f83daf9ca27e73307d97058 100644 (file)
@@ -1718,6 +1718,38 @@ defmodule Pleroma.UserTest do
     assert user.banner == %{}
   end
 
+  describe "set_suggestion" do
+    test "suggests a user" do
+      user = insert(:user, is_suggested: false)
+      refute user.is_suggested
+      {:ok, user} = User.set_suggestion(user, true)
+      assert user.is_suggested
+    end
+
+    test "suggests a list of users" do
+      unsuggested_users = [
+        insert(:user, is_suggested: false),
+        insert(:user, is_suggested: false),
+        insert(:user, is_suggested: false)
+      ]
+
+      {:ok, users} = User.set_suggestion(unsuggested_users, true)
+
+      assert Enum.count(users) == 3
+
+      Enum.each(users, fn user ->
+        assert user.is_suggested
+      end)
+    end
+
+    test "unsuggests a user" do
+      user = insert(:user, is_suggested: true)
+      assert user.is_suggested
+      {:ok, user} = User.set_suggestion(user, false)
+      refute user.is_suggested
+    end
+  end
+
   test "get_public_key_for_ap_id fetches a user that's not in the db" do
     assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
   end
@@ -2410,13 +2442,16 @@ defmodule Pleroma.UserTest do
   test "active_user_count/1" do
     insert(:user)
     insert(:user, %{local: false})
-    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -5)})
-    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -3)})
     insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), days: -15)})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -6)})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), months: -7)})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), years: -2)})
 
     assert User.active_user_count() == 2
-    assert User.active_user_count(6) == 3
-    assert User.active_user_count(1) == 1
+    assert User.active_user_count(180) == 3
+    assert User.active_user_count(365) == 4
+    assert User.active_user_count(1000) == 5
   end
 
   describe "pins" do
index a61244c7676657e3be92e4bd0bd91871240afb9a..574ef0d7155da69623db2c7488495e5735a87e4a 100644 (file)
@@ -776,6 +776,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     assert Enum.member?(activities, activity_one)
   end
 
+  test "doesn't return activities from deactivated users" do
+    _user = insert(:user)
+    deactivated = insert(:user)
+    active = insert(:user)
+    {:ok, activity_one} = CommonAPI.post(deactivated, %{status: "hey!"})
+    {:ok, activity_two} = CommonAPI.post(active, %{status: "yay!"})
+    {:ok, _updated_user} = User.set_activation(deactivated, false)
+
+    activities = ActivityPub.fetch_activities([], %{})
+
+    refute Enum.member?(activities, activity_one)
+    assert Enum.member?(activities, activity_two)
+  end
+
   test "always see your own posts even when they address people you block" do
     user = insert(:user)
     blockee = insert(:user)
index 0e49fda992219e5ca88e4dc3f3aef7e26e2aed00..9150b8d41ed1545e7807656d84fdc7eadca45438 100644 (file)
@@ -105,5 +105,37 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
 
       assert attachment.mediaType == "image/jpeg"
     end
+
+    test "it transforms image dimentions to our internal format" do
+      attachment = %{
+        "type" => "Document",
+        "name" => "Hello world",
+        "url" => "https://media.example.tld/1.jpg",
+        "width" => 880,
+        "height" => 960,
+        "mediaType" => "image/jpeg",
+        "blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
+      }
+
+      expected = %AttachmentValidator{
+        type: "Document",
+        name: "Hello world",
+        mediaType: "image/jpeg",
+        blurhash: "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of",
+        url: [
+          %AttachmentValidator.UrlObjectValidator{
+            type: "Link",
+            mediaType: "image/jpeg",
+            href: "https://media.example.tld/1.jpg",
+            width: 880,
+            height: 960
+          }
+        ]
+      }
+
+      {:ok, ^expected} =
+        AttachmentValidator.cast_and_validate(attachment)
+        |> Ecto.Changeset.apply_action(:insert)
+    end
   end
 end
index d0988619dcade410815840c611ec9e0585f80621..c6155ed18d1191f854abc29ebb54c5da27299236 100644 (file)
@@ -88,6 +88,16 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
       assert User.blocks?(user, blocked)
     end
 
+    test "it updates following relationship", %{user: user, blocked: blocked, block: block} do
+      {:ok, _, _} = SideEffects.handle(block)
+
+      refute Pleroma.FollowingRelationship.get(user, blocked)
+      assert User.get_follow_state(user, blocked) == nil
+      assert User.get_follow_state(blocked, user) == nil
+      assert User.get_follow_state(user, blocked, nil) == nil
+      assert User.get_follow_state(blocked, user, nil) == nil
+    end
+
     test "it blocks but does not unfollow if the relevant setting is set", %{
       user: user,
       blocked: blocked,
@@ -542,4 +552,74 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
       end
     end
   end
+
+  describe "removing a follower" do
+    setup do
+      user = insert(:user)
+      followed = insert(:user)
+
+      {:ok, _, _, follow_activity} = CommonAPI.follow(user, followed)
+
+      {:ok, reject_data, []} = Builder.reject(followed, follow_activity)
+      {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true)
+
+      %{user: user, followed: followed, reject: reject}
+    end
+
+    test "", %{user: user, followed: followed, reject: reject} do
+      assert User.following?(user, followed)
+      assert Pleroma.FollowingRelationship.get(user, followed)
+
+      {:ok, _, _} = SideEffects.handle(reject)
+
+      refute User.following?(user, followed)
+      refute Pleroma.FollowingRelationship.get(user, followed)
+      assert User.get_follow_state(user, followed) == nil
+      assert User.get_follow_state(user, followed, nil) == nil
+    end
+  end
+
+  describe "removing a follower from remote" do
+    setup do
+      user = insert(:user)
+      followed = insert(:user, local: false)
+
+      # Mock a local-to-remote follow
+      {:ok, follow_data, []} = Builder.follow(user, followed)
+
+      follow_data =
+        follow_data
+        |> Map.put("state", "accept")
+
+      {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true)
+      {:ok, _, _} = SideEffects.handle(follow)
+
+      # Mock a remote-to-local accept
+      {:ok, accept_data, _} = Builder.accept(followed, follow)
+      {:ok, accept, _} = ActivityPub.persist(accept_data, local: false)
+      {:ok, _, _} = SideEffects.handle(accept)
+
+      # Mock a remote-to-local reject
+      {:ok, reject_data, []} = Builder.reject(followed, follow)
+      {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: false)
+
+      %{user: user, followed: followed, reject: reject}
+    end
+
+    test "", %{user: user, followed: followed, reject: reject} do
+      assert User.following?(user, followed)
+      assert Pleroma.FollowingRelationship.get(user, followed)
+
+      {:ok, _, _} = SideEffects.handle(reject)
+
+      refute User.following?(user, followed)
+      refute Pleroma.FollowingRelationship.get(user, followed)
+
+      assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] ==
+               "reject"
+
+      assert User.get_follow_state(user, followed) == nil
+      assert User.get_follow_state(user, followed, nil) == nil
+    end
+  end
 end
index fc3ec7450f880c750259cc7972a15ee07441e250..87c53ebf42512d46a9b418ee3dd2dfa6bad867d9 100644 (file)
@@ -58,7 +58,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest do
                    "href" =>
                      "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
                    "mediaType" => "video/mp4",
-                   "type" => "Link"
+                   "type" => "Link",
+                   "width" => 480
                  }
                ]
              }
@@ -79,7 +80,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest do
                    "href" =>
                      "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4",
                    "mediaType" => "video/mp4",
-                   "type" => "Link"
+                   "type" => "Link",
+                   "height" => 1080
                  }
                ]
              }
@@ -107,7 +109,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest do
                    "href" =>
                      "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4",
                    "mediaType" => "video/mp4",
-                   "type" => "Link"
+                   "type" => "Link",
+                   "height" => 1080
                  }
                ]
              }
index 5a3b57acb84d1923ac2d950f1c022105a93d2ffb..06daf6a9fc7b6af5173116ad21ee433bc08fbf22 100644 (file)
@@ -524,4 +524,44 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
                )
     end
   end
+
+  describe "fix_attachments/1" do
+    test "puts dimensions into attachment url field" do
+      object = %{
+        "attachment" => [
+          %{
+            "type" => "Document",
+            "name" => "Hello world",
+            "url" => "https://media.example.tld/1.jpg",
+            "width" => 880,
+            "height" => 960,
+            "mediaType" => "image/jpeg",
+            "blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
+          }
+        ]
+      }
+
+      expected = %{
+        "attachment" => [
+          %{
+            "type" => "Document",
+            "name" => "Hello world",
+            "url" => [
+              %{
+                "type" => "Link",
+                "mediaType" => "image/jpeg",
+                "href" => "https://media.example.tld/1.jpg",
+                "width" => 880,
+                "height" => 960
+              }
+            ],
+            "mediaType" => "image/jpeg",
+            "blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
+          }
+        ]
+      }
+
+      assert Transmogrifier.fix_attachments(object) == expected
+    end
+  end
 end
index ee3e1014e29e1d550ad40a8b76ffca7077e8f8ca..62dc02f61fe66105445ea62ac3c7c4bf5857e940 100644 (file)
@@ -213,6 +213,20 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
       assert refresh_record(follow_activity).data["state"] == "accept"
       assert refresh_record(follow_activity_two).data["state"] == "accept"
     end
+
+    test "also updates the state of accepted follows" do
+      user = insert(:user)
+      follower = insert(:user)
+
+      {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
+      {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
+
+      {:ok, follow_activity_two} =
+        Utils.update_follow_state_for_all(follow_activity_two, "reject")
+
+      assert refresh_record(follow_activity).data["state"] == "reject"
+      assert refresh_record(follow_activity_two).data["state"] == "reject"
+    end
   end
 
   describe "update_follow_state/2" do
index d9da34f6ed5209d49d4253a0dfa23eecb18043ed..b199fa70450a7446e40cef7cb52913827db790f2 100644 (file)
@@ -873,6 +873,56 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
              "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
   end
 
+  test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do
+    user1 = insert(:user, is_suggested: false)
+    user2 = insert(:user, is_suggested: false)
+
+    response =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> patch(
+        "/api/pleroma/admin/users/suggest",
+        %{nicknames: [user1.nickname, user2.nickname]}
+      )
+      |> json_response_and_validate_schema(200)
+
+    assert Enum.map(response["users"], & &1["is_suggested"]) == [true, true]
+    [user1, user2] = Repo.reload!([user1, user2])
+
+    assert user1.is_suggested
+    assert user2.is_suggested
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{user2.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do
+    user1 = insert(:user, is_suggested: true)
+    user2 = insert(:user, is_suggested: true)
+
+    response =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> patch(
+        "/api/pleroma/admin/users/unsuggest",
+        %{nicknames: [user1.nickname, user2.nickname]}
+      )
+      |> json_response_and_validate_schema(200)
+
+    assert Enum.map(response["users"], & &1["is_suggested"]) == [false, false]
+    [user1, user2] = Repo.reload!([user1, user2])
+
+    refute user1.is_suggested
+    refute user2.is_suggested
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}"
+  end
+
   test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
     user = insert(:user)
 
@@ -906,6 +956,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
       "display_name" => HTML.strip_tags(user.name || user.nickname),
       "is_confirmed" => true,
       "is_approved" => true,
+      "is_suggested" => false,
       "url" => user.ap_id,
       "registration_reason" => nil,
       "actor_type" => "Person",
diff --git a/test/pleroma/web/manifest_controller_test.exs b/test/pleroma/web/manifest_controller_test.exs
new file mode 100644 (file)
index 0000000..b7a4940
--- /dev/null
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ManifestControllerTest do
+  use Pleroma.Web.ConnCase
+
+  setup do
+    clear_config([:instance, :name], "Manifest Test")
+    clear_config([:manifest, :theme_color], "#ff0000")
+  end
+
+  test "manifest.json", %{conn: conn} do
+    conn = get(conn, "/manifest.json")
+    assert %{"name" => "Manifest Test", "theme_color" => "#ff0000"} = json_response(conn, 200)
+  end
+end
index 4f855ac5c696ef07ca3754e1ab04491c1c09b70b..966a4072d4665c92c42bd8a5247a62b49d05bb17 100644 (file)
@@ -922,6 +922,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
                |> json_response_and_validate_schema(200)
     end
 
+    test "following with subscription and unsubscribing" do
+      %{conn: conn} = oauth_access(["follow"])
+      followed = insert(:user)
+
+      ret_conn =
+        conn
+        |> put_req_header("content-type", "application/json")
+        |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true})
+
+      assert %{"id" => _id, "subscribing" => true} =
+               json_response_and_validate_schema(ret_conn, 200)
+
+      ret_conn =
+        conn
+        |> put_req_header("content-type", "application/json")
+        |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false})
+
+      assert %{"id" => _id, "subscribing" => false} =
+               json_response_and_validate_schema(ret_conn, 200)
+    end
+
     test "following / unfollowing errors", %{user: user, conn: conn} do
       # self follow
       conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
index 168966fc9d3a7878dc5c6e6bd87c527085f3d571..89273e67bd3251204ec0c8ee7af2067ec34064ea 100644 (file)
@@ -4,8 +4,11 @@
 
 defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
   use Pleroma.Web.ConnCase, async: true
+  alias Pleroma.UserRelationship
+  alias Pleroma.Web.CommonAPI
+  import Pleroma.Factory
 
-  setup do: oauth_access(["read"])
+  setup do: oauth_access(["read", "write"])
 
   test "returns empty result", %{conn: conn} do
     res =
@@ -15,4 +18,66 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
 
     assert res == []
   end
+
+  test "returns v2 suggestions", %{conn: conn} do
+    %{id: user_id} = insert(:user, is_suggested: true)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res
+  end
+
+  test "returns v2 suggestions excluding dismissed accounts", %{conn: conn} do
+    %{id: user_id} = insert(:user, is_suggested: true)
+
+    conn
+    |> delete("/api/v1/suggestions/#{user_id}")
+    |> json_response_and_validate_schema(200)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [] = res
+  end
+
+  test "returns v2 suggestions excluding blocked accounts", %{conn: conn, user: blocker} do
+    blocked = insert(:user, is_suggested: true)
+    {:ok, _} = CommonAPI.block(blocker, blocked)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [] = res
+  end
+
+  test "returns v2 suggestions excluding followed accounts", %{conn: conn, user: follower} do
+    followed = insert(:user, is_suggested: true)
+    {:ok, _, _, _} = CommonAPI.follow(follower, followed)
+
+    res =
+      conn
+      |> get("/api/v2/suggestions")
+      |> json_response_and_validate_schema(200)
+
+    assert [] = res
+  end
+
+  test "dismiss suggestion", %{conn: conn, user: source} do
+    target = insert(:user, is_suggested: true)
+
+    res =
+      conn
+      |> delete("/api/v1/suggestions/#{target.id}")
+      |> json_response_and_validate_schema(200)
+
+    assert res == %{}
+    assert UserRelationship.exists?(:suggestion_dismiss, source, target)
+  end
 end
index 9fe9d73bc7556e42b2b2c58865f76fa383e4f978..39b9b0cef5278faaef45f70bd1c08284c3c37766 100644 (file)
@@ -83,6 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         tags: [],
         is_admin: false,
         is_moderator: false,
+        is_suggested: false,
         hide_favorites: true,
         hide_followers: false,
         hide_follows: false,
@@ -183,6 +184,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         tags: [],
         is_admin: false,
         is_moderator: false,
+        is_suggested: false,
         hide_favorites: true,
         hide_followers: false,
         hide_follows: false,
@@ -268,6 +270,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       muting: false,
       muting_notifications: false,
       subscribing: false,
+      notifying: false,
       requested: false,
       domain_blocking: false,
       showing_reblogs: true,
@@ -294,6 +297,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
             muting: true,
             muting_notifications: true,
             subscribing: true,
+            notifying: true,
             showing_reblogs: false,
             id: to_string(other_user.id)
           }
diff --git a/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs b/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs
new file mode 100644 (file)
index 0000000..5aae36c
--- /dev/null
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SuggestionViewTest do
+  use Pleroma.DataCase, async: true
+  import Pleroma.Factory
+  alias Pleroma.Web.MastodonAPI.SuggestionView, as: View
+
+  test "show.json" do
+    user = insert(:user, is_suggested: true)
+    json = View.render("show.json", %{user: user, source: :staff, skip_visibility_check: true})
+
+    assert json.source == :staff
+    assert json.account.id == user.id
+  end
+
+  test "index.json" do
+    user1 = insert(:user, is_suggested: true)
+    user2 = insert(:user, is_suggested: true)
+    user3 = insert(:user, is_suggested: true)
+
+    [suggestion1, suggestion2, suggestion3] =
+      View.render("index.json", %{
+        users: [user1, user2, user3],
+        source: :staff,
+        skip_visibility_check: true
+      })
+
+    assert suggestion1.source == :staff
+    assert suggestion2.account.id == user2.id
+    assert suggestion3.account.url == user3.ap_id
+  end
+end
index a9342e6f02306906a19ce36fa1365aea9a9fd408..52379b86a6d3355cb0076afb47228afc198bde74 100644 (file)
@@ -94,7 +94,10 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
       "internal",
       ".well-known",
       "nodeinfo",
+      "manifest.json",
+      "auth",
       "proxy",
+      "phoenix",
       "test",
       "user_exists",
       "check_password"
index d007e3f2697d1f7fc40ffcef5f7e4044582c6ae4..b7cfde1f788d47035452a119f62a82565267b1f3 100644 (file)
@@ -48,6 +48,7 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do
     refute RateLimiter.disabled?(build_conn())
   end
 
+  @tag :erratic
   test "it restricts based on config values" do
     limiter_name = :test_plug_opts
     scale = 80
@@ -137,6 +138,7 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do
   end
 
   describe "unauthenticated users" do
+    @tag :erratic
     test "are restricted based on remote IP" do
       limiter_name = :test_unauthenticated
       clear_config([:rate_limit, limiter_name], [{1000, 5}, {1, 10}])
@@ -174,6 +176,7 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do
       :ok
     end
 
+    @tag :erratic
     test "can have limits separate from unauthenticated connections" do
       limiter_name = :test_authenticated1
 
@@ -199,6 +202,7 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do
       assert conn.halted
     end
 
+    @tag :erratic
     test "different users are counted independently" do
       limiter_name = :test_authenticated2
       clear_config([:rate_limit, limiter_name], [{1, 10}, {1000, 5}])
index cf99e243418252239ae99b3414d23e2f59a8279f..45ab10a8aa6742f3dda63d4b555124c526c9ad58 100644 (file)
@@ -5,10 +5,14 @@
 defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
   use Pleroma.Web.ConnCase
 
+  alias Pleroma.Config
   alias Pleroma.PasswordResetToken
+  alias Pleroma.Repo
+  alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.Web.OAuth.Token
   import Pleroma.Factory
+  import Swoosh.TestAssertions
 
   describe "GET /api/pleroma/password_reset/token" do
     test "it returns error when token invalid", %{conn: conn} do
@@ -116,4 +120,94 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
       assert User.get_by_id(user.id).password_reset_pending == false
     end
   end
+
+  describe "POST /auth/password, with valid parameters" do
+    setup %{conn: conn} do
+      user = insert(:user)
+      conn = post(conn, "/auth/password?email=#{user.email}")
+      %{conn: conn, user: user}
+    end
+
+    test "it returns 204", %{conn: conn} do
+      assert empty_json_response(conn)
+    end
+
+    test "it creates a PasswordResetToken record for user", %{user: user} do
+      token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
+      assert token_record
+    end
+
+    test "it sends an email to user", %{user: user} do
+      ObanHelpers.perform_all()
+      token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
+
+      email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
+      notify_email = Config.get([:instance, :notify_email])
+      instance_name = Config.get([:instance, :name])
+
+      assert_email_sent(
+        from: {instance_name, notify_email},
+        to: {user.name, user.email},
+        html_body: email.html_body
+      )
+    end
+  end
+
+  describe "POST /auth/password, with nickname" do
+    test "it returns 204", %{conn: conn} do
+      user = insert(:user)
+
+      assert conn
+             |> post("/auth/password?nickname=#{user.nickname}")
+             |> empty_json_response()
+
+      ObanHelpers.perform_all()
+      token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
+
+      email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
+      notify_email = Config.get([:instance, :notify_email])
+      instance_name = Config.get([:instance, :name])
+
+      assert_email_sent(
+        from: {instance_name, notify_email},
+        to: {user.name, user.email},
+        html_body: email.html_body
+      )
+    end
+
+    test "it doesn't fail when a user has no email", %{conn: conn} do
+      user = insert(:user, %{email: nil})
+
+      assert conn
+             |> post("/auth/password?nickname=#{user.nickname}")
+             |> empty_json_response()
+    end
+  end
+
+  describe "POST /auth/password, with invalid parameters" do
+    setup do
+      user = insert(:user)
+      {:ok, user: user}
+    end
+
+    test "it returns 204 when user is not found", %{conn: conn, user: user} do
+      conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
+
+      assert empty_json_response(conn)
+    end
+
+    test "it returns 204 when user is not local", %{conn: conn, user: user} do
+      {:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false))
+      conn = post(conn, "/auth/password?email=#{user.email}")
+
+      assert empty_json_response(conn)
+    end
+
+    test "it returns 204 when user is deactivated", %{conn: conn, user: user} do
+      {:ok, user} = Repo.update(Ecto.Changeset.change(user, is_active: false, local: true))
+      conn = post(conn, "/auth/password?email=#{user.email}")
+
+      assert empty_json_response(conn)
+    end
+  end
 end
index 3380aec2234df8dc87d787d94f8ee0dc9d8ee8d0..ee658ddf64f6f780ae10a27f82fc0d9b69f7790d 100644 (file)
@@ -473,7 +473,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
 
     test "with proper permissions and wrong or missing password", %{conn: conn} do
       for params <- [%{"password" => "hi"}, %{}] do
-        ret_conn = post(conn, "/api/pleroma/delete_account", params)
+        ret_conn =
+          conn
+          |> put_req_header("content-type", "application/json")
+          |> post("/api/pleroma/delete_account", params)
 
         assert json_response_and_validate_schema(ret_conn, 200) == %{
                  "error" => "Invalid password."
@@ -481,8 +484,28 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       end
     end
 
-    test "with proper permissions and valid password", %{conn: conn, user: user} do
-      conn = post(conn, "/api/pleroma/delete_account?password=test")
+    test "with proper permissions and valid password (URL query)", %{conn: conn, user: user} do
+      conn =
+        conn
+        |> put_req_header("content-type", "application/json")
+        |> post("/api/pleroma/delete_account?password=test")
+
+      ObanHelpers.perform_all()
+      assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
+
+      user = User.get_by_id(user.id)
+      refute user.is_active
+      assert user.name == nil
+      assert user.bio == ""
+      assert user.password_hash == nil
+    end
+
+    test "with proper permissions and valid password (JSON body)", %{conn: conn, user: user} do
+      conn =
+        conn
+        |> put_req_header("content-type", "application/json")
+        |> post("/api/pleroma/delete_account", %{password: "test"})
+
       ObanHelpers.perform_all()
       assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
 
index 0c9783076afe64041c133589c18c534a1ef78f85..9fb41e985a60f3fa725260469778762610c2886b 100644 (file)
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []
-ExUnit.start(exclude: [:federated | os_exclude])
+ExUnit.start(exclude: [:federated, :erratic] ++ os_exclude)
 
 Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)