Merge branch 'develop' into refactor/deactivated_user_field
authorMark Felder <feld@feld.me>
Mon, 25 Jan 2021 15:45:06 +0000 (09:45 -0600)
committerMark Felder <feld@feld.me>
Mon, 25 Jan 2021 15:45:06 +0000 (09:45 -0600)
1  2 
CHANGELOG.md
lib/pleroma/user.ex
lib/pleroma/user/query.ex
test/pleroma/user_test.exs
test/pleroma/web/common_api_test.exs

diff --combined CHANGELOG.md
index f462477832f6544bcd1bf1f5f929cfdc5598baa2,2727d1f2cd0bb7e7036799df5a3476f319eba2c8..ea70b8df592ba5653e95b181a4430b51a93025d9
@@@ -11,13 -11,13 +11,14 @@@ The format is based on [Keep a Changelo
  - **Breaking:** Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
  - **Breaking**: AdminAPI changed User field `confirmation_pending` to `is_confirmed`
  - **Breaking**: AdminAPI changed User field `approval_pending` to `is_approved`
 +- **Breaking**: AdminAPI changed User field `deactivated` to `is_active`
  - Polls now always return a `voters_count`, even if they are single-choice.
  - Admin Emails: The ap id is used as the user link in emails now.
  - Improved registration workflow for email confirmation and account approval modes.
  - Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries.
  - Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators.
  - Admin API: Reports now ordered by newest
+ - Deprecated `Pleroma.Uploaders.S3, :public_endpoint`. Now `Pleroma.Upload, :base_url` is the standard configuration key for all uploaders.
  
  ### Added
  
@@@ -34,6 -34,8 +35,8 @@@
  - OAuth form improvements: users are remembered by their cookie, the CSS is overridable by the admin, and the style has been improved.
  - OAuth improvements and fixes: more secure session-based authentication (by token that could be revoked anytime), ability to revoke belonging OAuth token from any client etc.
  - Ability to set ActivityPub aliases for follower migration.
+ - Configurable background job limits for RichMedia (link previews) and MediaProxyWarmingPolicy
  
  <details>
    <summary>API Changes</summary>
@@@ -51,6 -53,8 +54,8 @@@
  - Users with `is_discoverable` field set to false (default value) will appear in in-service search results but be hidden from external services (search bots etc.).
  - Streaming API: Posts and notifications are not dropped, when CLI task is executing.
  - Creating incorrect IPv4 address-style HTTP links when encountering certain numbers.
+ - Reblog API Endpoint: Do not set visibility parameter to public by default and let CommonAPI to infer it from status, so a user can reblog their private status without explicitly setting reblog visibility to private.
+ - Tag URLs in statuses are now absolute
  
  <details>
    <summary>API Changes</summary>
  
  ## Unreleased (Patch)
  
+ ## [2.2.2] - 2020-01-18
  ### Fixed
  - StealEmojiPolicy creates dir for emojis, if it doesn't exist.
+ - Updated `elixir_make` to a non-retired version
+ ### Upgrade notes
+ 1. Restart Pleroma
  
  ## [2.2.1] - 2020-12-22
  
diff --combined lib/pleroma/user.ex
index b27923975161851f67adf7c768be9a427912bb6d,2aeacf8160ade0629dde2e3feb69de80b6a43361..e422b59f18c8d0d63148f0d4007b46d6c721d453
@@@ -117,7 -117,7 +117,7 @@@ defmodule Pleroma.User d
      field(:confirmation_token, :string, default: nil)
      field(:default_scope, :string, default: "public")
      field(:domain_blocks, {:array, :string}, default: [])
 -    field(:deactivated, :boolean, default: false)
 +    field(:is_active, :boolean, default: true)
      field(:no_rich_text, :boolean, default: false)
      field(:ap_enabled, :boolean, default: false)
      field(:is_moderator, :boolean, default: false)
        target_users_query = assoc(user, unquote(outgoing_relation_target))
  
        if restrict_deactivated? do
 -        restrict_deactivated(target_users_query)
 +        target_users_query
 +        |> User.Query.build(%{deactivated: false})
        else
          target_users_query
        end
  
    @doc "Returns status account"
    @spec account_status(User.t()) :: account_status()
 -  def account_status(%User{deactivated: true}), do: :deactivated
 +  def account_status(%User{is_active: false}), do: :deactivated
    def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
    def account_status(%User{local: true, is_approved: false}), do: :approval_pending
-   def account_status(%User{local: true, is_confirmed: false}) do
-     if Config.get([:instance, :account_activation_required]) do
-       :confirmation_pending
-     else
-       :active
-     end
-   end
+   def account_status(%User{local: true, is_confirmed: false}), do: :confirmation_pending
    def account_status(%User{}), do: :active
  
    @spec visible_for(User.t(), User.t() | nil) ::
    def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
    def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
  
 -  @spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
 -  def restrict_deactivated(query) do
 -    from(u in query, where: u.deactivated != ^true)
 -  end
 -
    defp truncate_fields_param(params) do
      if Map.has_key?(params, :fields) do
        Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
      candidates = Config.get([:instance, :autofollowed_nicknames])
  
      autofollowed_users =
 -      User.Query.build(%{nickname: candidates, local: true, deactivated: false})
 +      User.Query.build(%{nickname: candidates, local: true, is_active: true})
        |> Repo.all()
  
      follow_all(user, autofollowed_users)
      deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
  
      cond do
 -      followed.deactivated ->
 +      not followed.is_active ->
          {:error, "Could not follow user: #{followed.nickname} is deactivated."}
  
        deny_follow_blocked and blocks?(followed, follower) ->
  
    @spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
    def get_followers_query(%User{} = user, nil) do
 -    User.Query.build(%{followers: user, deactivated: false})
 +    User.Query.build(%{followers: user, is_active: true})
    end
  
    def get_followers_query(%User{} = user, page) do
    @spec get_users_from_set([String.t()], keyword()) :: [User.t()]
    def get_users_from_set(ap_ids, opts \\ []) do
      local_only = Keyword.get(opts, :local_only, true)
 -    criteria = %{ap_id: ap_ids, deactivated: false}
 +    criteria = %{ap_id: ap_ids, is_active: true}
      criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria
  
      User.Query.build(criteria)
    def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do
      to = [actor | to]
  
 -    query = User.Query.build(%{recipients_from_activity: to, local: true, deactivated: false})
 +    query = User.Query.build(%{recipients_from_activity: to, local: true, is_active: true})
  
      query
      |> Repo.all()
  
    defp maybe_filter_on_ap_id(query, _ap_ids), do: query
  
 -  def deactivate_async(user, status \\ true) do
 -    BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status})
 +  def set_activation_async(user, status \\ true) do
 +    BackgroundWorker.enqueue("user_activation", %{"user_id" => user.id, "status" => status})
    end
  
 -  def deactivate(user, status \\ true)
 -
 -  def deactivate(users, status) when is_list(users) do
 +  @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
 +  def set_activation(users, status) when is_list(users) do
      Repo.transaction(fn ->
 -      for user <- users, do: deactivate(user, status)
 +      for user <- users, do: set_activation(user, status)
      end)
    end
  
 -  def deactivate(%User{} = user, status) do
 +  @spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
 +  def set_activation(%User{} = user, status) do
      with {:ok, user} <- set_activation_status(user, status) do
        user
        |> get_followers()
        registration_reason: nil,
        confirmation_token: nil,
        domain_blocks: [],
 -      deactivated: true,
 +      is_active: false,
        ap_enabled: false,
        is_moderator: false,
        is_admin: false,
      delete_or_deactivate(user)
    end
  
 -  def perform(:deactivate_async, user, status), do: deactivate(user, status)
 +  def perform(:set_activation_async, user, status), do: set_activation(user, status)
  
    @spec external_users_query() :: Ecto.Query.t()
    def external_users_query do
  
    @spec all_superusers() :: [User.t()]
    def all_superusers do
 -    User.Query.build(%{super_users: true, local: true, deactivated: false})
 +    User.Query.build(%{super_users: true, local: true, is_active: true})
      |> Repo.all()
    end
  
        left_join: a in Pleroma.Activity,
        on: u.ap_id == a.actor,
        where: not is_nil(u.nickname),
 -      where: u.deactivated != ^true,
 +      where: u.is_active == ^true,
        where: u.id not in ^has_read_notifications,
        group_by: u.id,
        having:
    end
  
    # Internal function; public one is `deactivate/2`
 -  defp set_activation_status(user, deactivated) do
 +  defp set_activation_status(user, status) do
      user
 -    |> cast(%{deactivated: deactivated}, [:deactivated])
 +    |> cast(%{is_active: status}, [:is_active])
      |> update_and_set_cache()
    end
  
index e277f5fc2053204d184ef9d5f0faad84bd03d59c,4076925aaa1853a73ee5ce1680c1c1a8e3595786..fa46545dad167478f6705394e00145adeaf35fb8
@@@ -137,8 -137,9 +137,9 @@@ defmodule Pleroma.User.Query d
    defp compose_query({:external, _}, query), do: location_query(query, false)
  
    defp compose_query({:active, _}, query) do
 -    User.restrict_deactivated(query)
 +    where(query, [u], u.is_active == true)
      |> where([u], u.is_approved == true)
+     |> where([u], u.is_confirmed == true)
    end
  
    defp compose_query({:legacy_active, _}, query) do
    end
  
    defp compose_query({:deactivated, false}, query) do
 -    User.restrict_deactivated(query)
 +    where(query, [u], u.is_active == true)
    end
  
    defp compose_query({:deactivated, true}, query) do
 -    where(query, [u], u.deactivated == ^true)
 +    where(query, [u], u.is_active == false)
    end
  
    defp compose_query({:confirmation_pending, bool}, query) do
index 6449e4b4d6697ff6d5e591f5392604518c5326b9,7e1e75404f9c7213ab90c1f8df811b34b5167d38..90fef34bda538f447dcfbf46079f1d33e5db366e
@@@ -202,11 -202,11 +202,11 @@@ defmodule Pleroma.UserTest d
  
    test "doesn't return follow requests for deactivated accounts" do
      locked = insert(:user, is_locked: true)
 -    pending_follower = insert(:user, %{deactivated: true})
 +    pending_follower = insert(:user, %{is_active: false})
  
      CommonAPI.follow(pending_follower, locked)
  
 -    assert true == pending_follower.deactivated
 +    refute pending_follower.is_active
      assert [] = User.get_follow_requests(locked)
    end
  
  
    test "can't follow a deactivated users" do
      user = insert(:user)
 -    followed = insert(:user, %{deactivated: true})
 +    followed = insert(:user, %{is_active: false})
  
      {:error, _} = User.follow(user, followed)
    end
      end
    end
  
 -  describe ".deactivate" do
 +  describe ".set_activation" do
      test "can de-activate then re-activate a user" do
        user = insert(:user)
 -      assert false == user.deactivated
 -      {:ok, user} = User.deactivate(user)
 -      assert true == user.deactivated
 -      {:ok, user} = User.deactivate(user, false)
 -      assert false == user.deactivated
 +      assert user.is_active
 +      {:ok, user} = User.set_activation(user, false)
 +      refute user.is_active
 +      {:ok, user} = User.set_activation(user, true)
 +      assert user.is_active
      end
  
      test "hide a user from followers" do
        user2 = insert(:user)
  
        {:ok, user, user2} = User.follow(user, user2)
 -      {:ok, _user} = User.deactivate(user)
 +      {:ok, _user} = User.set_activation(user, false)
  
        user2 = User.get_cached_by_id(user2.id)
  
        assert user2.following_count == 1
        assert User.following_count(user2) == 1
  
 -      {:ok, _user} = User.deactivate(user)
 +      {:ok, _user} = User.set_activation(user, false)
  
        user2 = User.get_cached_by_id(user2.id)
  
                   user: user2
                 })
  
 -      {:ok, _user} = User.deactivate(user)
 +      {:ok, _user} = User.set_activation(user, false)
  
        assert [] == ActivityPub.fetch_public_activities(%{})
        assert [] == Pleroma.Notification.for_user(user2)
        follower = User.get_cached_by_id(follower.id)
  
        refute User.following?(follower, user)
 -      assert %{deactivated: true} = User.get_by_id(user.id)
 +      assert %{is_active: false} = User.get_by_id(user.id)
  
        assert [] == User.get_follow_requests(locked_user)
  
      end
    end
  
-   describe "delete/1 when confirmation is pending" do
-     setup do
-       user = insert(:user, is_confirmed: false)
-       {:ok, user: user}
-     end
-     test "deletes user from database when activation required", %{user: user} do
-       clear_config([:instance, :account_activation_required], true)
-       {:ok, job} = User.delete(user)
-       {:ok, _} = ObanHelpers.perform(job)
-       refute User.get_cached_by_id(user.id)
-       refute User.get_by_id(user.id)
-     end
+   test "delete/1 when confirmation is pending deletes the user" do
+     clear_config([:instance, :account_activation_required], true)
+     user = insert(:user, is_confirmed: false)
  
-     test "deactivates user when activation is not required", %{user: user} do
-       clear_config([:instance, :account_activation_required], false)
-       {:ok, job} = User.delete(user)
-       {:ok, _} = ObanHelpers.perform(job)
+     {:ok, job} = User.delete(user)
+     {:ok, _} = ObanHelpers.perform(job)
  
-       assert %{is_active: false} = User.get_cached_by_id(user.id)
-       assert %{is_active: false} = User.get_by_id(user.id)
-     end
+     refute User.get_cached_by_id(user.id)
+     refute User.get_by_id(user.id)
    end
  
    test "delete/1 when approval is pending deletes the user" do
          registration_reason: "ahhhhh",
          confirmation_token: "qqqq",
          domain_blocks: ["lain.com"],
 -        deactivated: true,
 +        is_active: false,
          ap_enabled: true,
          is_moderator: true,
          is_admin: true,
               registration_reason: nil,
               confirmation_token: nil,
               domain_blocks: [],
 -             deactivated: true,
 +             is_active: false,
               ap_enabled: false,
               is_moderator: false,
               is_admin: false,
      end
  
      test "returns :deactivated for deactivated user" do
 -      user = insert(:user, local: true, is_confirmed: true, deactivated: true)
 +      user = insert(:user, local: true, is_confirmed: true, is_active: false)
        assert User.account_status(user) == :deactivated
      end
  
        assert User.visible_for(user, other_user) == :visible
      end
  
-     test "returns true when the account is unconfirmed and confirmation is not required" do
-       user = insert(:user, local: true, is_confirmed: false)
-       other_user = insert(:user, local: true)
-       assert User.visible_for(user, other_user) == :visible
-     end
      test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do
        Pleroma.Config.put([:instance, :account_activation_required], true)
  
  
        users =
          Enum.map(1..total, fn _ ->
 -          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
 +          insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
          end)
  
        inactive_users_ids =
  
        users =
          Enum.map(1..total, fn _ ->
 -          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
 +          insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
          end)
  
        {inactive, active} = Enum.split(users, trunc(total / 2))
  
        users =
          Enum.map(1..total, fn _ ->
 -          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
 +          insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
          end)
  
        [sender | recipients] = users
        user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed")
        user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
        insert(:user, local: true)
 -      insert(:user, local: false, deactivated: true)
 +      insert(:user, local: false, is_active: false)
        {:ok, user1: user1, user2: user2}
      end
  
index 9d5a3d1191bf4c94d5704794c3bad2cd3b460170,7067f1b59092ac9a840bf28be2331fce74a3afca..c996766ea5ff726457d05a9bbbbdb6e2f14fa99b
@@@ -518,7 -518,7 +518,7 @@@ defmodule Pleroma.Web.CommonAPITest d
      end
  
      test "deactivated users can't post" do
 -      user = insert(:user, deactivated: true)
 +      user = insert(:user, is_active: false)
        assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
      end
  
        refute Visibility.visible_for_user?(announce_activity, nil)
      end
  
+     test "author can repeat own private statuses" do
+       author = insert(:user)
+       follower = insert(:user)
+       CommonAPI.follow(follower, author)
+       {:ok, activity} = CommonAPI.post(author, %{status: "cofe", visibility: "private"})
+       {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, author)
+       assert Visibility.is_private?(announce_activity)
+       refute Visibility.visible_for_user?(announce_activity, nil)
+       assert Visibility.visible_for_user?(activity, follower)
+       assert {:error, :not_found} = CommonAPI.repeat(activity.id, follower)
+     end
      test "favoriting a status" do
        user = insert(:user)
        other_user = insert(:user)