diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index a902c57e3..1d9f40ee0 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -103,6 +103,15 @@ defmodule Pleroma.User do |> validate_length(:name, min: 1, max: 100) end + def upgrade_changeset(struct, params \\ %{}) do + struct + |> cast(params, [:bio, :name, :info, :follower_address]) + |> unique_constraint(:nickname) + |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) + |> validate_length(:bio, min: 1, max: 1000) + |> validate_length(:name, min: 1, max: 100) + end + def password_update_changeset(struct, params) do changeset = struct |> cast(params, [:password, :password_confirmation]) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 554d3a008..0de730410 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -260,7 +260,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do Repo.insert(%Object{data: data}) end - def make_user_from_ap_id(ap_id) do + def fetch_and_prepare_user_from_ap_id(ap_id) do with {:ok, %{status_code: 200, body: body}} <- @httpoison.get(ap_id, ["Accept": "application/activity+json"]), {:ok, data} <- Poison.decode(body) do @@ -271,14 +271,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do "source_data" => data }, nickname: "#{data["preferredUsername"]}@#{URI.parse(ap_id).host}", - name: data["name"] + name: data["name"], + follower_address: data["followers"] } - if user = User.get_by_ap_id(ap_id) do - User.info_changeset(user, user_data) - |> Repo.update + {:ok, user_data} + end + end + + def make_user_from_ap_id(ap_id) do + if user = User.get_by_ap_id(ap_id) do + Transmogrifier.upgrade_user_from_ap_id(ap_id) + else + with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do + User.insert_or_update_user(data) else - User.insert_or_update_user(user_data) + e -> e end end end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index eb2569ef2..395436c5c 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -5,8 +5,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.User alias Pleroma.Object alias Pleroma.Activity + alias Pleroma.Repo alias Pleroma.Web.ActivityPub.ActivityPub + import Ecto.Query + @doc """ Modifies an incoming AP object (mastodon format) to our internal format. """ @@ -180,4 +183,33 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do object |> Map.put("attachment", attachments) end + + def upgrade_user_from_ap_id(ap_id) do + with %User{} = user <- User.get_by_ap_id(ap_id), + {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do + data = data + |> Map.put(:info, Map.merge(user.info, data[:info])) + + old_follower_address = user.follower_address + {:ok, user} = User.upgrade_changeset(user, data) + |> Repo.update() + + # This could potentially take a long time, do it in the background + Task.start(fn -> + q = from a in Activity, + where: ^old_follower_address in a.recipients, + update: [set: [recipients: fragment("array_replace(?,?,?)", a.recipients, ^old_follower_address, ^user.follower_address)]] + Repo.update_all(q, []) + + q = from u in User, + where: ^old_follower_address in u.following, + update: [set: [following: fragment("array_replace(?,?,?)", u.following, ^old_follower_address, ^user.follower_address)]] + Repo.update_all(q, []) + end) + + {:ok, user} + else + e -> e + end + end end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 96dd63057..185734852 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -178,4 +178,40 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert modified["object"]["actor"] == modified["object"]["attributedTo"] end end + + describe "user upgrade" do + test "it upgrades a user to activitypub" do + user = insert(:user, %{local: false, ap_id: "https://niu.moe/users/rye", follower_address: "..."}) + user_two = insert(:user, %{following: [user.follower_address]}) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) + {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) + assert "..." in activity.recipients + + user = Repo.get(User, user.id) + assert user.info["note_count"] == 1 + + {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye") + assert user.info["ap_enabled"] + assert user.info["note_count"] == 1 + assert user.follower_address == "https://niu.moe/users/rye/followers" + + # Wait for the background task + :timer.sleep(1000) + + user = Repo.get(User, user.id) + assert user.info["note_count"] == 1 + + activity = Repo.get(Activity, activity.id) + assert user.follower_address in activity.recipients + refute "..." in activity.recipients + + unrelated_activity = Repo.get(Activity, unrelated_activity.id) + refute user.follower_address in unrelated_activity.recipients + + user_two = Repo.get(User, user_two.id) + assert user.follower_address in user_two.following + refute "..." in user_two.following + end + end end