Merge remote-tracking branch 'pleroma/develop' into feature/disable-account
authorEgor Kislitsyn <egor@kislitsyn.com>
Thu, 11 Apr 2019 08:51:52 +0000 (15:51 +0700)
committerEgor Kislitsyn <egor@kislitsyn.com>
Thu, 11 Apr 2019 08:51:52 +0000 (15:51 +0700)
1  2 
config/config.exs
lib/mix/tasks/pleroma/user.ex
lib/pleroma/activity.ex
lib/pleroma/notification.ex
lib/pleroma/user.ex
lib/pleroma/user/info.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/controllers/util_controller.ex
lib/pleroma/web/twitter_api/twitter_api.ex

index d9ed43dda63530e9230e92455ae5f574feee2b0e,3462a37f76b3a6fd3517a4de4c2fbfcd3cc08f11..d42b136b772b09f549e9fa9b65e2a8125d5763a1
@@@ -340,11 -364,17 +364,18 @@@ config :pleroma, Pleroma.Web.Federator.
    initial_timeout: 30,
    max_retries: 5
  
- config :pleroma, Pleroma.Jobs,
-   federator_incoming: [max_jobs: 50],
-   federator_outgoing: [max_jobs: 50],
-   mailer: [max_jobs: 10],
-   user: [max_jobs: 10]
+ config :pleroma_job_queue, :queues,
+   federator_incoming: 50,
+   federator_outgoing: 50,
+   web_push: 50,
+   mailer: 10,
+   transmogrifier: 20,
 -  scheduled_activities: 10
++  scheduled_activities: 10,
++  user: 10
+ config :pleroma, :fetch_initial_posts,
+   enabled: false,
+   pages: 5
  
  config :auto_linker,
    opts: [
Simple merge
index c466bff7f0a834371fbb2a46299af6fb903f8285,ab8861b277b7683660e6ca031dd8f69db664522c..c8c7f0d040bd0726f50f26ef78634c7b9feb7526
@@@ -41,13 -79,44 +79,47 @@@ defmodule Pleroma.Activity d
      )
    end
  
+   def get_by_ap_id_with_object(ap_id) do
+     Repo.one(
+       from(
+         activity in Activity,
+         where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)),
+         left_join: o in Object,
+         on:
+           fragment(
+             "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
+             o.data,
+             activity.data,
+             activity.data
+           ),
+         preload: [object: o]
+       )
+     )
+   end
    def get_by_id(id) do
 -    Repo.get(Activity, id)
 +    Activity
 +    |> where([a], a.id == ^id)
 +    |> restrict_disabled_users()
 +    |> Repo.one()
    end
  
+   def get_by_id_with_object(id) do
+     from(activity in Activity,
+       where: activity.id == ^id,
+       inner_join: o in Object,
+       on:
+         fragment(
+           "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
+           o.data,
+           activity.data,
+           activity.data
+         ),
+       preload: [object: o]
+     )
+     |> Repo.one()
+   end
    def by_object_ap_id(ap_id) do
      from(
        activity in Activity,
      |> Repo.all()
    end
  
+   def increase_replies_count(id) do
+     Activity
+     |> where(id: ^id)
+     |> update([a],
+       set: [
+         data:
+           fragment(
+             """
+             jsonb_set(?, '{object, repliesCount}',
+               (coalesce((?->'object'->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true)
+             """,
+             a.data,
+             a.data
+           )
+       ]
+     )
+     |> Repo.update_all([])
+     |> case do
+       {1, [activity]} -> activity
+       _ -> {:error, "Not found"}
+     end
+   end
+   def decrease_replies_count(id) do
+     Activity
+     |> where(id: ^id)
+     |> update([a],
+       set: [
+         data:
+           fragment(
+             """
+             jsonb_set(?, '{object, repliesCount}',
+               (greatest(0, (?->'object'->>'repliesCount')::int - 1))::varchar::jsonb, true)
+             """,
+             a.data,
+             a.data
+           )
+       ]
+     )
+     |> Repo.update_all([])
+     |> case do
+       {1, [activity]} -> activity
+       _ -> {:error, "Not found"}
+     end
+   end
++
 +  def restrict_disabled_users(query) do
 +    from(activity in query,
 +      where:
 +        fragment(
 +          "? not in (SELECT ap_id FROM users WHERE info->'disabled' @> 'true')",
 +          activity.actor
 +        )
 +    )
 +  end
  end
index 0f9f74b1ebacbea9f15b5d6f488c435bf11ef3d0,15789907a0071a716b037170603fe2221b5c4fee..7de2d4c189b47633fffd97aac4fd553f77b6f959
@@@ -22,36 -25,30 +25,37 @@@ defmodule Pleroma.Notification d
      timestamps()
    end
  
-   # TODO: Make generic and unify (see activity_pub.ex)
-   defp restrict_max(query, %{"max_id" => max_id}) do
-     from(activity in query, where: activity.id < ^max_id)
-   end
-   defp restrict_max(query, _), do: query
-   defp restrict_since(query, %{"since_id" => since_id}) do
-     from(activity in query, where: activity.id > ^since_id)
+   def changeset(%Notification{} = notification, attrs) do
+     notification
+     |> cast(attrs, [:seen])
    end
  
-   defp restrict_since(query, _), do: query
-   def for_user(user, opts \\ %{}) do
-     from(
-       n in Notification,
-       where: n.user_id == ^user.id,
-       order_by: [desc: n.id],
-       join: activity in assoc(n, :activity),
-       preload: [activity: activity],
-       limit: 20,
-       where:
+   def for_user_query(user) do
+     Notification
+     |> where(user_id: ^user.id)
++    |> where(
++      [n, a],
++      fragment(
++        "? not in (SELECT ap_id FROM users WHERE info->'disabled' @> 'true')",
++        a.actor
++      )
++    )
+     |> join(:inner, [n], activity in assoc(n, :activity))
+     |> join(:left, [n, a], object in Object,
+       on:
          fragment(
-           "? not in (SELECT ap_id FROM users WHERE info->'disabled' @> 'true')",
-           activity.actor
+           "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
+           object.data,
+           a.data
          )
      )
-     |> restrict_since(opts)
-     |> restrict_max(opts)
-     |> Repo.all()
+     |> preload([n, a, o], activity: {a, object: o})
+   end
+   def for_user(user, opts \\ %{}) do
+     user
+     |> for_user_query()
+     |> Pagination.fetch_paginated(opts)
    end
  
    def set_read_up_to(%{id: user_id} = _user, id) do
index f02051174ac2d6909f3ae9ff3bb2ca62610cc368,6e2269aff220030a3ef81de3fde2ab58911a4d87..1f2aca235b05d9710c6a045e4831f060b1e43528
@@@ -103,13 -103,14 +103,12 @@@ defmodule Pleroma.User d
      "#{Web.base_url()}/users/#{nickname}"
    end
  
-   def ap_followers(%User{} = user) do
-     "#{ap_id(user)}/followers"
-   end
+   def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
+   def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
  
    def user_info(%User{} = user) do
 -    oneself = if user.local, do: 1, else: 0
 -
      %{
 -      following_count: length(user.following) - oneself,
 +      following_count: following_count(user),
        note_count: user.info.note_count,
        follower_count: user.info.follower_count,
        locked: user.info.locked,
        },
        where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
      )
 +    |> restrict_disabled()
    end
  
-   defp boost_search_results(results, nil), do: results
-   defp boost_search_results(results, for_user) do
-     friends_ids = get_friends_ids(for_user)
-     followers_ids = get_followers_ids(for_user)
-     Enum.map(
-       results,
-       fn u ->
-         search_rank_coef =
-           cond do
-             u.id in friends_ids ->
-               1.2
-             u.id in followers_ids ->
-               1.1
-             true ->
-               1
-           end
-         Map.put(u, :search_rank, u.search_rank * search_rank_coef)
-       end
-     )
-     |> Enum.sort_by(&(-&1.search_rank))
-   end
    def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
      Enum.map(
        blocked_identifiers,
    def deactivate(%User{} = user, status \\ true) do
      info_cng = User.Info.set_activation_status(user.info, status)
  
 -    cng =
 -      change(user)
 -      |> put_embed(:info, info_cng)
 -
 -    update_and_set_cache(cng)
 +    user
 +    |> change()
 +    |> put_embed(:info, info_cng)
 +    |> update_and_set_cache()
    end
  
+   def update_notification_settings(%User{} = user, settings \\ %{}) do
+     info_changeset = User.Info.update_notification_settings(user.info, settings)
+     change(user)
+     |> put_embed(:info, info_changeset)
+     |> update_and_set_cache()
+   end
    def delete(%User{} = user) do
      {:ok, user} = User.deactivate(user)
  
      {:ok, user}
    end
  
-     Pleroma.Jobs.enqueue(:user, __MODULE__, [:disable_async, user, status])
 +  def disable_async(user, status \\ true) do
++    PleromaJobQueue.enqueue(:user, __MODULE__, [:disable_async, user, status])
 +  end
 +
 +  def disable(%User{} = user, status \\ true) do
 +    with {:ok, user} <- User.deactivate(user, status),
 +         info_cng <- User.Info.set_disabled_status(user.info, status),
 +         {:ok, user} <-
 +           user
 +           |> change()
 +           |> put_embed(:info, info_cng)
 +           |> update_and_set_cache(),
 +         {:ok, friends} <- User.get_friends(user) do
 +      Enum.each(friends, &update_follower_count(&1))
 +      {:ok, user}
 +    end
 +  end
 +
 +  def perform(:disable_async, user, status), do: disable(user, status)
 +
    def html_filter_policy(%User{info: %{no_rich_text: true}}) do
      Pleroma.HTML.Scrubber.TwitterText
    end
index 1ec356ba90af6eb5d3a282053a43bbb69313101c,5afa7988cee7287750e2f6e1a0169732c12cd486..07825a1c4bc1ec5362c634014f1211c4abaf952a
@@@ -36,8 -40,11 +40,12 @@@ defmodule Pleroma.User.Info d
      field(:hide_follows, :boolean, default: false)
      field(:pinned_activities, {:array, :string}, default: [])
      field(:flavour, :string, default: nil)
 +    field(:disabled, :boolean, default: false)
  
+     field(:notification_settings, :map,
+       default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
+     )
      # Found in the wild
      # ap_id -> Where is this used?
      # bio -> Where is this used?
      |> validate_required([:deactivated])
    end
  
+   def update_notification_settings(info, settings) do
+     notification_settings =
+       info.notification_settings
+       |> Map.merge(settings)
+       |> Map.take(["remote", "local", "followers", "follows"])
+     params = %{notification_settings: notification_settings}
+     info
+     |> cast(params, [:notification_settings])
+     |> validate_required([:notification_settings])
+   end
 +  def set_disabled_status(info, disabled) do
 +    params = %{disabled: disabled}
 +
 +    info
 +    |> cast(params, [:disabled])
 +    |> validate_required([:disabled])
 +  end
 +
    def add_to_note_count(info, number) do
      set_note_count(info, info.note_count + number)
    end
index aa20990f3ddb4276446299235ba9e63fe3a4178a,f217e7bac35c9271f633c3765ddb9477769d1e31..dd51d63c8bb446db6ed01434680fb22a8bca2fa6
@@@ -703,7 -803,7 +803,8 @@@ defmodule Pleroma.Web.ActivityPub.Activ
      |> restrict_replies(opts)
      |> restrict_reblogs(opts)
      |> restrict_pinned(opts)
+     |> restrict_muted_reblogs(opts)
 +    |> Activity.restrict_disabled_users()
    end
  
    def fetch_activities(recipients, opts \\ %{}) do
index 1b94f0609d5d32dff77c896beb0e85225523c8c7,70a5b5c5d7dbf4cfaf3821f9978c0453c0345175..fb43d0b011674c0f9748594c376eba7150ca241b
@@@ -44,16 -66,15 +66,25 @@@ defmodule Pleroma.Web.AdminAPI.AdminAPI
      |> json(user.nickname)
    end
  
+   def user_show(conn, %{"nickname" => nickname}) do
+     with %User{} = user <- User.get_by_nickname(nickname) do
+       conn
+       |> json(AccountView.render("show.json", %{user: user}))
+     else
+       _ -> {:error, :not_found}
+     end
+   end
 +  def user_toggle_disabled(conn, %{"nickname" => nickname}) do
 +    user = User.get_by_nickname(nickname)
 +
 +    {:ok, updated_user} = User.disable(user, !user.info.disabled)
 +
 +    conn
 +    |> put_view(AccountView)
 +    |> render("show.json", %{user: updated_user})
 +  end
 +
    def user_toggle_activation(conn, %{"nickname" => nickname}) do
      user = User.get_by_nickname(nickname)
  
index 5033b5446af44e4a41337efa0ef3555331fb3fd8,172f337db6556d6aba71dd3ea6ea0ea47022d68c..dd23d7fd5a68fee4f6719fb0f0f666fdd4113eef
@@@ -139,11 -145,14 +145,15 @@@ defmodule Pleroma.Web.Router d
    scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
      pipe_through([:admin_api, :oauth_write])
  
+     post("/user/follow", AdminAPIController, :user_follow)
+     post("/user/unfollow", AdminAPIController, :user_unfollow)
      get("/users", AdminAPIController, :list_users)
-     get("/users/search", AdminAPIController, :search_users)
+     get("/users/:nickname", AdminAPIController, :user_show)
      delete("/user", AdminAPIController, :user_delete)
      patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
 +    patch("/users/:nickname/toggle_disabled", AdminAPIController, :user_toggle_disabled)
      post("/user", AdminAPIController, :user_create)
      put("/users/tag", AdminAPIController, :tag_users)
      delete("/users/tag", AdminAPIController, :untag_users)
  
        post("/change_password", UtilController, :change_password)
        post("/delete_account", UtilController, :delete_account)
+       put("/notification_settings", UtilController, :update_notificaton_settings)
 +      post("/disable_account", UtilController, :disable_account)
      end
  
      scope [] do