Mastodon API: Add a setting to hide follow/follower count from the user view (`hide_f...
authoreugenijm <eugenijm@protonmail.com>
Fri, 13 Sep 2019 14:37:30 +0000 (17:37 +0300)
committereugenijm <eugenijm@protonmail.com>
Tue, 17 Sep 2019 11:45:47 +0000 (14:45 +0300)
CHANGELOG.md
docs/api/differences_in_mastoapi_responses.md
lib/pleroma/user/info.ex
lib/pleroma/web/activity_pub/views/user_view.ex
lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
test/web/activity_pub/views/user_view_test.exs
test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs
test/web/mastodon_api/views/account_view_test.exs

index 4eb72c002cb128fb6539f43d47e7b36cc832cdfe..7dfa477b4912385c1b06a404cd6b0108f66b44b9 100644 (file)
@@ -94,6 +94,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
 - Mastodon API: /api/v1/accounts/:id/statuses now supports nicknames or user id
 - Mastodon API: Improve support for the user profile custom fields
+- Mastodon API: follower/following counters are nullified when `hide_follows`/`hide_followers` and `hide_follows_count`/`hide_followers_count` are set
 - Admin API: Return users' tags when querying reports
 - Admin API: Return avatar and display name when querying users
 - Admin API: Allow querying user by ID
index 9b32baf3a4a70e92f9596fba2c92224fd363d589..3c7f5dad7ff429899b8559910fb6e44eeef299b4 100644 (file)
@@ -50,6 +50,8 @@ Has these additional fields under the `pleroma` object:
 - `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
 - `hide_followers`: boolean, true when the user has follower hiding enabled
 - `hide_follows`: boolean, true when the user has follow hiding enabled
+- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
+- `hide_follows_count`: boolean, true when the user has follow stat hiding enabled
 - `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
 - `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
 - `deactivated`: boolean, true when the user is deactivated
@@ -112,6 +114,8 @@ Additional parameters can be added to the JSON body/Form data:
 - `no_rich_text` - if true, html tags are stripped from all statuses requested from the API
 - `hide_followers` - if true, user's followers will be hidden
 - `hide_follows` - if true, user's follows will be hidden
+- `hide_followers_count` - if true, user's follower count will be hidden
+- `hide_follows_count` - if true, user's follow count will be hidden
 - `hide_favorites` - if true, user's favorites timeline will be hidden
 - `show_role` - if true, user's role (e.g admin, moderator) will be exposed to anyone in the API
 - `default_scope` - the scope returned under `privacy` key in Source subentity
index 151e025de8bf98f1e9da6af1844a505a74cb3019..b150a57cd80e5b440d96f8f8c4fad684b84b9103 100644 (file)
@@ -41,6 +41,8 @@ defmodule Pleroma.User.Info do
     field(:topic, :string, default: nil)
     field(:hub, :string, default: nil)
     field(:salmon, :string, default: nil)
+    field(:hide_followers_count, :boolean, default: false)
+    field(:hide_follows_count, :boolean, default: false)
     field(:hide_followers, :boolean, default: false)
     field(:hide_follows, :boolean, default: false)
     field(:hide_favorites, :boolean, default: true)
@@ -262,6 +264,8 @@ defmodule Pleroma.User.Info do
       :salmon,
       :hide_followers,
       :hide_follows,
+      :hide_followers_count,
+      :hide_follows_count,
       :follower_count,
       :fields,
       :following_count
@@ -281,7 +285,9 @@ defmodule Pleroma.User.Info do
       :following_count,
       :hide_follows,
       :fields,
-      :hide_followers
+      :hide_followers,
+      :hide_followers_count,
+      :hide_follows_count
     ])
     |> validate_fields(remote?)
   end
@@ -295,6 +301,8 @@ defmodule Pleroma.User.Info do
       :banner,
       :hide_follows,
       :hide_followers,
+      :hide_followers_count,
+      :hide_follows_count,
       :hide_favorites,
       :background,
       :show_role,
@@ -458,7 +466,9 @@ defmodule Pleroma.User.Info do
       :hide_followers,
       :hide_follows,
       :follower_count,
-      :following_count
+      :following_count,
+      :hide_followers_count,
+      :hide_follows_count
     ])
   end
 end
index 7be734b2604a73e917814a81364c43259e19b4f9..164b973d0816dbc4fdc4b3a7a6a1f284c987bfd1 100644 (file)
@@ -118,30 +118,34 @@ defmodule Pleroma.Web.ActivityPub.UserView do
   end
 
   def render("following.json", %{user: user, page: page} = opts) do
-    showing = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
+    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
+    showing_count = showing_items || !user.info.hide_follows_count
+
     query = User.get_friends_query(user)
     query = from(user in query, select: [:ap_id])
     following = Repo.all(query)
 
     total =
-      if showing do
+      if showing_count do
         length(following)
       else
         0
       end
 
-    collection(following, "#{user.ap_id}/following", page, showing, total)
+    collection(following, "#{user.ap_id}/following", page, showing_items, total)
     |> Map.merge(Utils.make_json_ld_header())
   end
 
   def render("following.json", %{user: user} = opts) do
-    showing = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
+    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
+    showing_count = showing_items || !user.info.hide_follows_count
+
     query = User.get_friends_query(user)
     query = from(user in query, select: [:ap_id])
     following = Repo.all(query)
 
     total =
-      if showing do
+      if showing_count do
         length(following)
       else
         0
@@ -152,7 +156,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
       "type" => "OrderedCollection",
       "totalItems" => total,
       "first" =>
-        if showing do
+        if showing_items do
           collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
         else
           "#{user.ap_id}/following?page=1"
@@ -162,32 +166,34 @@ defmodule Pleroma.Web.ActivityPub.UserView do
   end
 
   def render("followers.json", %{user: user, page: page} = opts) do
-    showing = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
+    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
+    showing_count = showing_items || !user.info.hide_followers_count
 
     query = User.get_followers_query(user)
     query = from(user in query, select: [:ap_id])
     followers = Repo.all(query)
 
     total =
-      if showing do
+      if showing_count do
         length(followers)
       else
         0
       end
 
-    collection(followers, "#{user.ap_id}/followers", page, showing, total)
+    collection(followers, "#{user.ap_id}/followers", page, showing_items, total)
     |> Map.merge(Utils.make_json_ld_header())
   end
 
   def render("followers.json", %{user: user} = opts) do
-    showing = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
+    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
+    showing_count = showing_items || !user.info.hide_followers_count
 
     query = User.get_followers_query(user)
     query = from(user in query, select: [:ap_id])
     followers = Repo.all(query)
 
     total =
-      if showing do
+      if showing_count do
         length(followers)
       else
         0
@@ -198,8 +204,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
       "type" => "OrderedCollection",
       "totalItems" => total,
       "first" =>
-        if showing do
-          collection(followers, "#{user.ap_id}/followers", 1, showing, total)
+        if showing_items do
+          collection(followers, "#{user.ap_id}/followers", 1, showing_items, total)
         else
           "#{user.ap_id}/followers?page=1"
         end
index 060137b80e38fe45945c6d8d34892e3830f86c0b..1beb4bcf28fa49462b195bfbb9ae47a97daa75af 100644 (file)
@@ -147,6 +147,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       [
         :no_rich_text,
         :locked,
+        :hide_followers_count,
+        :hide_follows_count,
         :hide_followers,
         :hide_follows,
         :hide_favorites,
index 169116d0d421bae977c5f0abb00d319bb574e739..195dd124b272644948998ffaaec77e7859b8c79c 100644 (file)
@@ -74,10 +74,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
     user_info = User.get_cached_user_info(user)
 
     following_count =
-      ((!user.info.hide_follows or opts[:for] == user) && user_info.following_count) || 0
+      if !user.info.hide_follows_count or !user.info.hide_follows or opts[:for] == user do
+        user_info.following_count
+      else
+        0
+      end
 
     followers_count =
-      ((!user.info.hide_followers or opts[:for] == user) && user_info.follower_count) || 0
+      if !user.info.hide_followers_count or !user.info.hide_followers or opts[:for] == user do
+        user_info.follower_count
+      else
+        0
+      end
 
     bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]
 
@@ -138,6 +146,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       pleroma: %{
         confirmation_pending: user_info.confirmation_pending,
         tags: user.tags,
+        hide_followers_count: user.info.hide_followers_count,
+        hide_follows_count: user.info.hide_follows_count,
         hide_followers: user.info.hide_followers,
         hide_follows: user.info.hide_follows,
         hide_favorites: user.info.hide_favorites,
index fb7fd9e79f6bbf4fd0ff87a5c5b4c06ffe4fa136..2b4a04afdf516d16d0edb9e0600123ad8f51f6f8 100644 (file)
@@ -105,10 +105,20 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
       other_user = insert(:user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
       assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
-      info = Map.put(user.info, :hide_followers, true)
+      info = Map.merge(user.info, %{hide_followers_count: true, hide_followers: true})
       user = Map.put(user, :info, info)
       assert %{"totalItems" => 0} = UserView.render("followers.json", %{user: user})
     end
+
+    test "sets correct totalItems when followers are hidden but the follower counter is not" do
+      user = insert(:user)
+      other_user = insert(:user)
+      {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
+      assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
+      info = Map.merge(user.info, %{hide_followers_count: false, hide_followers: true})
+      user = Map.put(user, :info, info)
+      assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
+    end
   end
 
   describe "following" do
@@ -117,9 +127,19 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
       other_user = insert(:user)
       {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
       assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
-      info = Map.put(user.info, :hide_follows, true)
+      info = Map.merge(user.info, %{hide_follows_count: true, hide_follows: true})
       user = Map.put(user, :info, info)
       assert %{"totalItems" => 0} = UserView.render("following.json", %{user: user})
     end
+
+    test "sets correct totalItems when follows are hidden but the follow counter is not" do
+      user = insert(:user)
+      other_user = insert(:user)
+      {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
+      assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
+      info = Map.merge(user.info, %{hide_follows_count: false, hide_follows: true})
+      user = Map.put(user, :info, info)
+      assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
+    end
   end
 end
index 87ee82050cf47c65296354cbe4d903558a8d8466..89d4ca37eccd12727cb51f230b97df8b7e7ed2bd 100644 (file)
@@ -128,6 +128,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
       assert user["pleroma"]["hide_followers"] == true
     end
 
+    test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do
+      user = insert(:user)
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> patch("/api/v1/accounts/update_credentials", %{
+          hide_followers_count: "true",
+          hide_follows_count: "true"
+        })
+
+      assert user = json_response(conn, 200)
+      assert user["pleroma"]["hide_followers_count"] == true
+      assert user["pleroma"]["hide_follows_count"] == true
+    end
+
     test "updates the user's skip_thread_containment option", %{conn: conn} do
       user = insert(:user)
 
index 1d8b2833929820cb389d292257c31c9d3d63d94d..8ff6751d3623875d40650e9af49babd1d3fb04a1 100644 (file)
@@ -79,6 +79,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         hide_favorites: true,
         hide_followers: false,
         hide_follows: false,
+        hide_followers_count: false,
+        hide_follows_count: false,
         relationship: %{},
         skip_thread_containment: false
       }
@@ -147,6 +149,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         hide_favorites: true,
         hide_followers: false,
         hide_follows: false,
+        hide_followers_count: false,
+        hide_follows_count: false,
         relationship: %{},
         skip_thread_containment: false
       }
@@ -318,6 +322,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         hide_favorites: true,
         hide_followers: false,
         hide_follows: false,
+        hide_followers_count: false,
+        hide_follows_count: false,
         relationship: %{
           id: to_string(user.id),
           following: false,
@@ -361,8 +367,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
   end
 
   describe "hiding follows/following" do
-    test "shows when follows/following are hidden and sets follower/following count to 0" do
-      user = insert(:user, info: %{hide_followers: true, hide_follows: true})
+    test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
+      info = %{
+        hide_followers: true,
+        hide_followers_count: true,
+        hide_follows: true,
+        hide_follows_count: true
+      }
+
+      user = insert(:user, info: info)
+
       other_user = insert(:user)
       {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
@@ -370,6 +384,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       assert %{
                followers_count: 0,
                following_count: 0,
+               pleroma: %{hide_follows_count: true, hide_followers_count: true}
+             } = AccountView.render("account.json", %{user: user})
+    end
+
+    test "shows when follows/followers are hidden" do
+      user = insert(:user, info: %{hide_followers: true, hide_follows: true})
+      other_user = insert(:user)
+      {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
+      {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
+
+      assert %{
+               followers_count: 1,
+               following_count: 1,
                pleroma: %{hide_follows: true, hide_followers: true}
              } = AccountView.render("account.json", %{user: user})
     end