1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do
9 create_if_not_exists table(:following_relationships) do
10 add(:follower_id, references(:users, type: :uuid, on_delete: :delete_all), null: false)
11 add(:following_id, references(:users, type: :uuid, on_delete: :delete_all), null: false)
12 add(:state, :string, null: false)
17 create_if_not_exists(index(:following_relationships, :follower_id))
18 create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id]))
20 execute(update_thread_visibility(), restore_thread_visibility())
23 # The only difference between the original version: `actor_user` replaced with `actor_user_following`
24 def update_thread_visibility do
26 CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$
28 public varchar := 'https://www.w3.org/ns/activitystreams#Public';
29 child objects%ROWTYPE;
30 activity activities%ROWTYPE;
32 valid_recipients varchar[];
33 actor_user_following varchar[];
35 --- Fetch actor following
36 SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships
37 JOIN users ON users.id = following_relationships.follower_id
38 JOIN users AS following ON following.id = following_relationships.following_id
39 WHERE users.ap_id = actor;
41 --- Fetch our initial activity.
42 SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id;
45 --- Ensure that we have an activity before continuing.
46 --- If we don't, the thread is not satisfiable.
47 IF activity IS NULL THEN
51 --- We only care about Create activities.
52 IF activity.data->>'type' != 'Create' THEN
56 --- Normalize the child object into child.
57 SELECT * INTO child FROM objects
58 INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
59 WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id';
61 --- Fetch the author's AS2 following collection.
62 SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor;
64 --- Prepare valid recipients array.
65 valid_recipients := ARRAY[actor, public];
66 IF ARRAY[author_fa] && actor_user_following THEN
67 valid_recipients := valid_recipients || author_fa;
71 IF NOT valid_recipients && activity.recipients THEN
72 --- activity not visible, break out of the loop
76 --- If there's a parent, load it and do this all over again.
77 IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN
78 SELECT * INTO activity FROM activities
79 INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
80 WHERE child.data->>'inReplyTo' = objects.data->>'id';
86 $$ LANGUAGE plpgsql IMMUTABLE;
90 # priv/repo/migrations/20190515222404_add_thread_visibility_function.exs
91 def restore_thread_visibility do
93 CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$
95 public varchar := 'https://www.w3.org/ns/activitystreams#Public';
96 child objects%ROWTYPE;
97 activity activities%ROWTYPE;
98 actor_user users%ROWTYPE;
100 valid_recipients varchar[];
103 SELECT * INTO actor_user FROM users WHERE users.ap_id = actor;
105 --- Fetch our initial activity.
106 SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id;
109 --- Ensure that we have an activity before continuing.
110 --- If we don't, the thread is not satisfiable.
111 IF activity IS NULL THEN
115 --- We only care about Create activities.
116 IF activity.data->>'type' != 'Create' THEN
120 --- Normalize the child object into child.
121 SELECT * INTO child FROM objects
122 INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
123 WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id';
125 --- Fetch the author's AS2 following collection.
126 SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor;
128 --- Prepare valid recipients array.
129 valid_recipients := ARRAY[actor, public];
130 IF ARRAY[author_fa] && actor_user.following THEN
131 valid_recipients := valid_recipients || author_fa;
134 --- Check visibility.
135 IF NOT valid_recipients && activity.recipients THEN
136 --- activity not visible, break out of the loop
140 --- If there's a parent, load it and do this all over again.
141 IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN
142 SELECT * INTO activity FROM activities
143 INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
144 WHERE child.data->>'inReplyTo' = objects.data->>'id';
150 $$ LANGUAGE plpgsql IMMUTABLE;