diff options
-rw-r--r-- | lib/pleroma/user.ex | 109 | ||||
-rw-r--r-- | lib/pleroma/user/info.ex | 1 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 48 | ||||
-rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api_controller.ex | 1 | ||||
-rw-r--r-- | lib/pleroma/web/twitter_api/views/user_view.ex | 9 | ||||
-rw-r--r-- | priv/repo/migrations/20190303120636_update_user_note_counters.exs | 41 | ||||
-rw-r--r-- | test/web/activity_pub/activity_pub_test.exs | 51 | ||||
-rw-r--r-- | test/web/mastodon_api/mastodon_api_controller_test.exs | 4 | ||||
-rw-r--r-- | test/web/twitter_api/twitter_api_controller_test.exs | 27 |
9 files changed, 206 insertions, 85 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 06b430ccd..d69ca094e 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -621,63 +621,57 @@ defmodule Pleroma.User do ) end - def update_follow_request_count(%User{} = user) do - subquery = + def get_follow_requests(%User{} = user) do + users = user |> User.get_follow_requests_query() - |> select([a], %{count: count(a.id)}) + |> join(:inner, [a], u in User, a.actor == u.ap_id) + |> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address])) + |> group_by([a, u], u.id) + |> select([a, u], u) + |> Repo.all() + {:ok, users} + end + + def increase_note_count(%User{} = user) do User |> where(id: ^user.id) - |> join(:inner, [u], s in subquery(subquery)) - |> update([u, s], + |> update([u], set: [ info: fragment( - "jsonb_set(?, '{follow_request_count}', ?::varchar::jsonb, true)", + "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)", u.info, - s.count + u.info ) ] ) |> Repo.update_all([], returning: true) |> case do - {1, [user]} -> {:ok, user} + {1, [user]} -> set_cache(user) _ -> {:error, user} end end - def get_follow_requests(%User{} = user) do - users = - user - |> User.get_follow_requests_query() - |> join(:inner, [a], u in User, a.actor == u.ap_id) - |> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address])) - |> group_by([a, u], u.id) - |> select([a, u], u) - |> Repo.all() - - {:ok, users} - end - - def increase_note_count(%User{} = user) do - info_cng = User.Info.add_to_note_count(user.info, 1) - - cng = - change(user) - |> put_embed(:info, info_cng) - - update_and_set_cache(cng) - end - def decrease_note_count(%User{} = user) do - info_cng = User.Info.add_to_note_count(user.info, -1) - - cng = - change(user) - |> put_embed(:info, info_cng) - - update_and_set_cache(cng) + User + |> where(id: ^user.id) + |> update([u], + set: [ + info: + fragment( + "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)", + u.info, + u.info + ) + ] + ) + |> Repo.update_all([], returning: true) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end end def update_note_count(%User{} = user) do @@ -701,24 +695,29 @@ defmodule Pleroma.User do def update_follower_count(%User{} = user) do follower_count_query = - from( - u in User, - where: ^user.follower_address in u.following, - where: u.id != ^user.id, - select: count(u.id) - ) - - follower_count = Repo.one(follower_count_query) - - info_cng = - user.info - |> User.Info.set_follower_count(follower_count) + User + |> where([u], ^user.follower_address in u.following) + |> where([u], u.id != ^user.id) + |> select([u], %{count: count(u.id)}) - cng = - change(user) - |> put_embed(:info, info_cng) - - update_and_set_cache(cng) + User + |> where(id: ^user.id) + |> join(:inner, [u], s in subquery(follower_count_query)) + |> update([u, s], + set: [ + info: + fragment( + "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", + u.info, + s.count + ) + ] + ) + |> Repo.update_all([], returning: true) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end end def get_users_from_set_query(ap_ids, false) do diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 00a0f6df3..818b64645 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -12,7 +12,6 @@ defmodule Pleroma.User.Info do field(:source_data, :map, default: %{}) field(:note_count, :integer, default: 0) field(:follower_count, :integer, default: 0) - field(:follow_request_count, :integer, default: 0) field(:locked, :boolean, default: false) field(:confirmation_pending, :boolean, default: false) field(:confirmation_token, :string, default: nil) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 16ae65867..783491b67 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -81,6 +81,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp check_remote_limit(_), do: true + def increase_note_count_if_public(actor, object) do + if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor} + end + + def decrease_note_count_if_public(actor, object) do + if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} + end + def insert(map, local \\ true) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map), @@ -163,7 +171,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ), {:ok, activity} <- insert(create_data, local), # Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info - {:ok, _actor} <- User.increase_note_count(actor), + {:ok, _actor} <- increase_note_count_if_public(actor, activity), :ok <- maybe_federate(activity) do {:ok, activity} end @@ -175,8 +183,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object}, {:ok, activity} <- insert(data, local), - :ok <- maybe_federate(activity), - _ <- User.update_follow_request_count(actor) do + :ok <- maybe_federate(activity) do {:ok, activity} end end @@ -187,8 +194,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object}, {:ok, activity} <- insert(data, local), - :ok <- maybe_federate(activity), - _ <- User.update_follow_request_count(actor) do + :ok <- maybe_federate(activity) do {:ok, activity} end end @@ -286,8 +292,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def follow(follower, followed, activity_id \\ nil, local \\ true) do with data <- make_follow_data(follower, followed, activity_id), {:ok, activity} <- insert(data, local), - :ok <- maybe_federate(activity), - _ <- User.update_follow_request_count(followed) do + :ok <- maybe_federate(activity) do {:ok, activity} end end @@ -297,8 +302,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"), unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id), {:ok, activity} <- insert(unfollow_data, local), - :ok <- maybe_federate(activity), - _ <- User.update_follow_request_count(followed) do + :ok <- maybe_federate(activity) do {:ok, activity} end end @@ -316,7 +320,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do with {:ok, _} <- Object.delete(object), {:ok, activity} <- insert(data, local), # Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info - {:ok, _actor} <- User.decrease_note_count(user), + {:ok, _actor} <- decrease_note_count_if_public(user, object), :ok <- maybe_federate(activity) do {:ok, activity} end @@ -421,6 +425,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @valid_visibilities ~w[direct unlisted public private] defp restrict_visibility(query, %{visibility: visibility}) + when is_list(visibility) do + if Enum.all?(visibility, &(&1 in @valid_visibilities)) do + query = + from( + a in query, + where: + fragment( + "activity_visibility(?, ?, ?) = ANY (?)", + a.actor, + a.recipients, + a.data, + ^visibility + ) + ) + + Ecto.Adapters.SQL.to_sql(:all, Repo, query) + + query + else + Logger.error("Could not restrict visibility to #{visibility}") + end + end + + defp restrict_visibility(query, %{visibility: visibility}) when visibility in @valid_visibilities do query = from( diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 0d74c30c3..41e3acc60 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -167,6 +167,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do params |> Map.put("type", ["Create", "Announce", "Follow", "Like"]) |> Map.put("blocking_user", user) + |> Map.put(:visibility, ~w[unlisted public private]) activities = ActivityPub.fetch_activities([user.ap_id], params) diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index 22f33e0b5..e72ce977c 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -133,7 +133,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do "tags" => user.tags } |> maybe_with_activation_status(user, for_user) - |> maybe_with_follow_request_count(user, for_user) } data = @@ -155,14 +154,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do defp maybe_with_activation_status(data, _, _), do: data - defp maybe_with_follow_request_count(data, %User{id: id, info: %{locked: true}} = user, %User{ - id: id - }) do - Map.put(data, "follow_request_count", user.info.follow_request_count) - end - - defp maybe_with_follow_request_count(data, _, _), do: data - defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do Map.merge(data, %{"role" => role(user), "show_role" => user.info.show_role}) end diff --git a/priv/repo/migrations/20190303120636_update_user_note_counters.exs b/priv/repo/migrations/20190303120636_update_user_note_counters.exs new file mode 100644 index 000000000..54e68f7c9 --- /dev/null +++ b/priv/repo/migrations/20190303120636_update_user_note_counters.exs @@ -0,0 +1,41 @@ +defmodule Pleroma.Repo.Migrations.UpdateUserNoteCounters do + use Ecto.Migration + + @public "https://www.w3.org/ns/activitystreams#Public" + + def up do + execute """ + WITH public_note_count AS ( + SELECT + data->>'actor' AS actor, + count(id) AS count + FROM objects + WHERE data->>'type' = 'Note' AND ( + data->'cc' ? '#{@public}' OR data->'to' ? '#{@public}' + ) + GROUP BY data->>'actor' + ) + UPDATE users AS u + SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true) + FROM public_note_count AS o + WHERE u.ap_id = o.actor + """ + end + + def down do + execute """ + WITH public_note_count AS ( + SELECT + data->>'actor' AS actor, + count(id) AS count + FROM objects + WHERE data->>'type' = 'Note' + GROUP BY data->>'actor' + ) + UPDATE users AS u + SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true) + FROM public_note_count AS o + WHERE u.ap_id = o.actor + """ + end +end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index ac3a565de..f4029896c 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -55,6 +55,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id}) assert activities == [public_activity] + + activities = + ActivityPub.fetch_activities([], %{ + :visibility => ~w[private public], + "actor_id" => user.ap_id + }) + + assert activities == [public_activity, private_activity] end end @@ -205,6 +213,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity.actor == user.ap_id assert activity.recipients == ["user1", "user2", user.ap_id] end + + test "increases user note count only for public activities" do + user = insert(:user) + + {:ok, _} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "1", "visibility" => "public"}) + + {:ok, _} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "unlisted"}) + + {:ok, _} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "private"}) + + {:ok, _} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "3", "visibility" => "direct"}) + + user = Repo.get(User, user.id) + assert user.info.note_count == 2 + end end describe "fetch activities for recipients" do @@ -640,6 +667,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert Repo.get(Object, object.id).data["type"] == "Tombstone" end + + test "decrements user note count only for public activities" do + user = insert(:user, info: %{note_count: 10}) + + {:ok, a1} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "public"}) + + {:ok, a2} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "unlisted"}) + + {:ok, a3} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "private"}) + + {:ok, a4} = + CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "direct"}) + + {:ok, _} = a1.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete() + {:ok, _} = a2.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete() + {:ok, _} = a3.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete() + {:ok, _} = a4.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete() + + user = Repo.get(User, user.id) + assert user.info.note_count == 10 + end end describe "timeline post-processing" do diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index b52c2b805..f7f10662a 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -946,7 +946,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do other_user = Repo.get(User, other_user.id) assert User.following?(other_user, user) == false - assert user.info.follow_request_count == 1 conn = build_conn() @@ -960,7 +959,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do other_user = Repo.get(User, other_user.id) assert User.following?(other_user, user) == true - assert user.info.follow_request_count == 0 end test "verify_credentials", %{conn: conn} do @@ -982,7 +980,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do {:ok, _activity} = ActivityPub.follow(other_user, user) user = Repo.get(User, user.id) - assert user.info.follow_request_count == 1 conn = build_conn() @@ -996,7 +993,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do other_user = Repo.get(User, other_user.id) assert User.following?(other_user, user) == false - assert user.info.follow_request_count == 0 end end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 05a832967..d18b65876 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -427,7 +427,10 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do test "with credentials", %{conn: conn, user: current_user} do {:ok, activity} = - ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user}) + CommonAPI.post(current_user, %{ + "status" => "why is tenshi eating a corndog so cute?", + "visibility" => "public" + }) conn = conn @@ -445,6 +448,23 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do mentioned: [current_user] }) end + + test "does not show DMs in mentions timeline", %{conn: conn, user: current_user} do + {:ok, _activity} = + CommonAPI.post(current_user, %{ + "status" => "Have you guys ever seen how cute tenshi eating a corndog is?", + "visibility" => "direct" + }) + + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> get("/api/statuses/mentions.json") + + response = json_response(conn, 200) + + assert length(response) == 0 + end end describe "GET /api/qvitter/statuses/notifications.json" do @@ -670,7 +690,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do followed = Repo.get(User, followed.id) refute User.ap_followers(followed) in current_user.following - assert followed.info.follow_request_count == 1 assert json_response(conn, 200) == UserView.render("show.json", %{user: followed, for: current_user}) @@ -1737,7 +1756,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do other_user = Repo.get(User, other_user.id) assert User.following?(other_user, user) == false - assert user.info.follow_request_count == 1 conn = build_conn() @@ -1749,7 +1767,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do assert relationship = json_response(conn, 200) assert other_user.id == relationship["id"] assert relationship["follows_you"] == true - assert user.info.follow_request_count == 0 end end @@ -1764,7 +1781,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do other_user = Repo.get(User, other_user.id) assert User.following?(other_user, user) == false - assert user.info.follow_request_count == 1 conn = build_conn() @@ -1776,7 +1792,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do assert relationship = json_response(conn, 200) assert other_user.id == relationship["id"] assert relationship["follows_you"] == false - assert user.info.follow_request_count == 0 end end |