Merge pull request 'Add dark and light theme mode to docs, detection, and button...
[akkoma] / priv / repo / migrations / 20191029172832_fix_blocked_follows.exs
1 defmodule Pleroma.Repo.Migrations.FixBlockedFollows do
2 use Ecto.Migration
3
4 import Ecto.Query
5 alias Pleroma.Config
6 alias Pleroma.Repo
7
8 def up do
9 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
10
11 if unfollow_blocked do
12 "activities"
13 |> where([activity], fragment("? ->> 'type' = 'Block'", activity.data))
14 |> distinct([activity], [
15 activity.actor,
16 fragment(
17 "coalesce((?)->'object'->>'id', (?)->>'object')",
18 activity.data,
19 activity.data
20 )
21 ])
22 |> order_by([activity], [fragment("? desc nulls last", activity.id)])
23 |> select([activity], %{
24 blocker: activity.actor,
25 blocked:
26 fragment("coalesce((?)->'object'->>'id', (?)->>'object')", activity.data, activity.data),
27 created_at: activity.id
28 })
29 |> Repo.stream()
30 |> Enum.map(&unfollow_if_blocked/1)
31 |> Enum.uniq()
32 |> Enum.each(&update_follower_count/1)
33 end
34 end
35
36 def down do
37 end
38
39 def unfollow_if_blocked(%{blocker: blocker_id, blocked: blocked_id, created_at: blocked_at}) do
40 query =
41 from(
42 activity in "activities",
43 where: fragment("? ->> 'type' = 'Follow'", activity.data),
44 where: activity.actor == ^blocked_id,
45 # this is to use the index
46 where:
47 fragment(
48 "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
49 activity.data,
50 activity.data,
51 ^blocker_id
52 ),
53 where: activity.id > ^blocked_at,
54 where: fragment("(?)->>'state' = 'accept'", activity.data),
55 order_by: [fragment("? desc nulls last", activity.id)]
56 )
57
58 unless Repo.exists?(query) do
59 blocker = "users" |> select([:id, :local]) |> Repo.get_by(ap_id: blocker_id)
60 blocked = "users" |> select([:id]) |> Repo.get_by(ap_id: blocked_id)
61
62 if !is_nil(blocker) && !is_nil(blocked) do
63 unfollow(blocked, blocker)
64 end
65 end
66 end
67
68 def unfollow(%{id: follower_id}, %{id: followed_id} = followed) do
69 following_relationship =
70 "following_relationships"
71 |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept")
72 |> select([:id])
73 |> Repo.one()
74
75 case following_relationship do
76 nil ->
77 {:ok, nil}
78
79 %{id: following_relationship_id} ->
80 "following_relationships"
81 |> where(id: ^following_relationship_id)
82 |> Repo.delete_all()
83
84 followed
85 end
86 end
87
88 def update_follower_count(%{id: user_id} = user) do
89 if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
90 follower_count_query =
91 "users"
92 |> where([u], u.id != ^user_id)
93 |> where([u], u.deactivated != ^true)
94 |> join(:inner, [u], r in "following_relationships",
95 as: :relationships,
96 on: r.following_id == ^user_id and r.follower_id == u.id
97 )
98 |> where([relationships: r], r.state == "accept")
99 |> select([u], %{count: count(u.id)})
100
101 "users"
102 |> where(id: ^user_id)
103 |> join(:inner, [u], s in subquery(follower_count_query))
104 |> update([u, s],
105 set: [follower_count: s.count]
106 )
107 |> Repo.update_all([])
108 end
109 end
110
111 def update_follower_count(_), do: :noop
112 end