escape attachment urls on creation.
[akkoma] / lib / pleroma / user.ex
index 869a3eb1b5b77352b41b3920da7072ab488a2eeb..f28f0deb0cad55c2b418f444cef94b28522f77e3 100644 (file)
@@ -21,6 +21,7 @@ defmodule Pleroma.User do
     field :avatar, :map
     field :local, :boolean, default: true
     field :info, :map, default: %{}
+    field :follower_address, :string
 
     timestamps()
   end
@@ -46,32 +47,37 @@ defmodule Pleroma.User do
     |> validate_required([:following])
   end
 
-  def user_info(%User{} = user) do
-    note_count_query = from a in Object,
-      where: fragment("? @> ?", a.data, ^%{actor: user.ap_id, type: "Note"}),
-      select: count(a.id)
-
-    follower_count_query = from u in User,
-      where: fragment("? @> ?", u.following, ^User.ap_followers(user)),
-      select: count(u.id)
+  def info_changeset(struct, params \\ %{}) do
+    struct
+    |> cast(params, [:info])
+    |> validate_required([:info])
+  end
 
+  def user_info(%User{} = user) do
     %{
       following_count: length(user.following),
-      note_count: Repo.one(note_count_query),
-      follower_count: Repo.one(follower_count_query)
+      note_count: user.info["note_count"] || 0,
+      follower_count: user.info["follower_count"] || 0
     }
   end
 
   @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
   def remote_user_creation(params) do
-    %User{}
+    changes = %User{}
     |> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar])
     |> validate_required([:name, :ap_id, :nickname])
     |> unique_constraint(:nickname)
     |> validate_format(:nickname, @email_regex)
-    |> validate_length(:bio, max: 1000)
+    |> validate_length(:bio, max: 5000)
     |> validate_length(:name, max: 100)
     |> put_change(:local, false)
+    if changes.valid? do
+      followers = User.ap_followers(%User{nickname: changes.changes[:nickname]})
+      changes
+      |> put_change(:follower_address, followers)
+    else
+      changes
+    end
   end
 
   def register_changeset(struct, params \\ %{}) do
@@ -94,13 +100,14 @@ defmodule Pleroma.User do
       |> put_change(:password_hash, hashed)
       |> put_change(:ap_id, ap_id)
       |> put_change(:following, [followers])
+      |> put_change(:follower_address, followers)
     else
       changeset
     end
   end
 
   def follow(%User{} = follower, %User{} = followed) do
-    ap_followers = User.ap_followers(followed)
+    ap_followers = followed.follower_address
     if following?(follower, followed) do
       {:error,
        "Could not follow user: #{followed.nickname} is already on your list."}
@@ -112,14 +119,18 @@ defmodule Pleroma.User do
       following = [ap_followers | follower.following]
       |> Enum.uniq
 
-      follower
+      follower = follower
       |> follow_changeset(%{following: following})
       |> Repo.update
+
+      {:ok, followed} = update_follower_count(followed)
+
+      follower
     end
   end
 
   def unfollow(%User{} = follower, %User{} = followed) do
-    ap_followers = User.ap_followers(followed)
+    ap_followers = followed.follower_address
     if following?(follower, followed) do
       following = follower.following
       |> List.delete(ap_followers)
@@ -127,14 +138,17 @@ defmodule Pleroma.User do
       { :ok, follower } = follower
       |> follow_changeset(%{following: following})
       |> Repo.update
-      { :ok, follower, Utils.fetch_latest_follow(follower, followed)}
+
+      {:ok, followed} = update_follower_count(followed)
+
+      {:ok, follower, Utils.fetch_latest_follow(follower, followed)}
     else
       {:error, "Not subscribed!"}
     end
   end
 
   def following?(%User{} = follower, %User{} = followed) do
-    Enum.member?(follower.following, User.ap_followers(followed))
+    Enum.member?(follower.following, followed.follower_address)
   end
 
   def get_by_ap_id(ap_id) do
@@ -171,4 +185,49 @@ defmodule Pleroma.User do
       end
     end
   end
+
+  # TODO: these queries could be more efficient if the type in postgresql wasn't map, but array.
+  def get_followers(%User{id: id, follower_address: follower_address}) do
+    q = from u in User,
+      where: fragment("? @> ?", u.following, ^follower_address ),
+      where: u.id != ^id
+
+    {:ok, Repo.all(q)}
+  end
+
+  def get_friends(%User{id: id, following: following}) do
+    q = from u in User,
+      where: u.follower_address in ^following,
+      where: u.id != ^id
+
+    {:ok, Repo.all(q)}
+  end
+
+  def update_note_count(%User{} = user) do
+    note_count_query = from a in Object,
+      where: fragment("? @> ?", a.data, ^%{actor: user.ap_id, type: "Note"}),
+      select: count(a.id)
+
+    note_count = Repo.one(note_count_query)
+
+    new_info = Map.put(user.info, "note_count", note_count)
+
+    cs = info_changeset(user, %{info: new_info})
+
+    Repo.update(cs)
+  end
+
+  def update_follower_count(%User{} = user) do
+    follower_count_query = from u in User,
+      where: fragment("? @> ?", u.following, ^user.follower_address),
+      select: count(u.id)
+
+    follower_count = Repo.one(follower_count_query)
+
+    new_info = Map.put(user.info, "follower_count", follower_count)
+
+    cs = info_changeset(user, %{info: new_info})
+
+    Repo.update(cs)
+  end
 end