config :pleroma, Pleroma.Jobs,
federator_incoming: [max_jobs: 50],
federator_outgoing: [max_jobs: 50],
- mailer: [max_jobs: 10]
+ mailer: [max_jobs: 10],
+ user: [max_jobs: 10]
config :auto_linker,
opts: [
- `--password PASSWORD` - the user's password
- `--moderator`/`--no-moderator` - whether the user is a moderator
- `--admin`/`--no-admin` - whether the user is an admin
- - `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
+ - `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
## Generate an invite link.
mix pleroma.user toggle_activated NICKNAME
+ ## Disable or enable the user's account.
+
+ mix pleroma.user toggle_disabled NICKNAME
+
## Unsubscribe local users from user's account and deactivate it
mix pleroma.user unsubscribe NICKNAME
end
end
+ def run(["toggle_disabled", nickname]) do
+ Common.start_pleroma()
+
+ case User.get_by_nickname(nickname) do
+ %User{} = user ->
+ {:ok, user} = User.disable(user, !user.info.disabled)
+ status = if(user.info.disabled, do: "ON", else: "OFF")
+ Mix.shell().info("Disabled status of #{nickname}: #{status}")
+
+ _ ->
+ Mix.shell().error("No user #{nickname}")
+ end
+ end
+
def run(["reset_password", nickname]) do
Common.start_pleroma()
end
def get_by_id(id) do
- Repo.get(Activity, id)
+ Activity
+ |> where([a], a.id == ^id)
+ |> restrict_disabled_users()
+ |> Repo.one()
end
def by_object_ap_id(ap_id) do
def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do
create_by_object_ap_id(ap_id)
+ |> restrict_disabled_users()
|> Repo.one()
end
|> where([s], s.actor == ^actor)
|> Repo.all()
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
alias Pleroma.Activity
alias Pleroma.HTML
alias Pleroma.User
- alias Pleroma.Repo
def start_link(ref, socket, transport, opts) do
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
end
def response("/notices/" <> id) do
- with %Activity{} = activity <- Repo.get(Activity, id),
+ with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.is_public?(activity) do
activities =
ActivityPub.fetch_activities_for_context(activity.data["context"])
defp restrict_since(query, _), do: query
def for_user(user, opts \\ %{}) do
- query =
- 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
- )
-
- query =
- query
- |> restrict_since(opts)
- |> restrict_max(opts)
-
- Repo.all(query)
+ 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:
+ fragment(
+ "? not in (SELECT ap_id FROM users WHERE info->'disabled' @> 'true')",
+ activity.actor
+ )
+ )
+ |> restrict_since(opts)
+ |> restrict_max(opts)
+ |> Repo.all()
end
def set_read_up_to(%{id: user_id} = _user, id) do
end
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,
}
end
+ defp restrict_disabled(query) do
+ from(u in query,
+ where: not fragment("? \\? 'disabled' AND ?->'disabled' @> 'true'", u.info, u.info)
+ )
+ end
+
+ def following_count(%User{following: []}), do: 0
+
+ def following_count(%User{following: following, id: id}) do
+ from(u in User,
+ where: u.follower_address in ^following,
+ where: u.id != ^id
+ )
+ |> restrict_disabled()
+ |> Repo.aggregate(:count, :id)
+ end
+
def remote_user_creation(params) do
params =
params
where: fragment("? <@ ?", ^[follower_address], u.following),
where: u.id != ^id
)
+ |> restrict_disabled()
end
def get_followers_query(user, page) do
where: u.follower_address in ^following,
where: u.id != ^id
)
+ |> restrict_disabled()
end
def get_friends_query(user, page) do
info_cng = User.Info.set_note_count(user.info, note_count)
- 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_follower_count(%User{} = user) do
|> where([u], ^user.follower_address in u.following)
|> where([u], u.id != ^user.id)
|> select([u], %{count: count(u.id)})
+ |> restrict_disabled()
User
|> where(id: ^user.id)
^processed_query
)
)
+ |> restrict_disabled()
end
defp trigram_search_subquery(term) do
},
where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
)
+ |> restrict_disabled()
end
defp boost_search_results(results, nil), do: results
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 delete(%User{} = user) do
{:ok, user}
end
+ def disable_async(user, status \\ true) do
+ Pleroma.Jobs.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
field(:hide_follows, :boolean, default: false)
field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil)
+ field(:disabled, :boolean, default: false)
# Found in the wild
# ap_id -> Where is this used?
|> validate_required([:deactivated])
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
|> restrict_replies(opts)
|> restrict_reblogs(opts)
|> restrict_pinned(opts)
+ |> Activity.restrict_disabled_users()
end
def fetch_activities(recipients, opts \\ %{}) do
|> json(user.nickname)
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)
# This is a hack for twidere.
def get_by_id_or_ap_id(id) do
- activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
+ activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id)
activity &&
if activity.data["type"] == "Create" do
def get_replied_to_activity(""), do: nil
def get_replied_to_activity(id) when not is_nil(id) do
- Repo.get(Activity, id)
+ Activity.get_by_id(id)
end
def get_replied_to_activity(_), do: nil
end
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Repo.get(Activity, id),
+ with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.visible_for_user?(activity, user) do
conn
|> put_view(StatusView)
end
def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Repo.get(Activity, id),
+ with %Activity{} = activity <- Activity.get_by_id(id),
activities <-
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
"blocking_user" => user,
end
def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Repo.get(Activity, id),
+ with %Activity{} = activity <- Activity.get_by_id(id),
%User{} = user <- User.get_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
{:ok, user} <- User.bookmark(user, activity.data["object"]["id"]) do
end
def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Repo.get(Activity, id),
+ with %Activity{} = activity <- Activity.get_by_id(id),
%User{} = user <- User.get_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
{:ok, user} <- User.unbookmark(user, activity.data["object"]["id"]) do
end
def favourited_by(conn, %{"id" => id}) do
- with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do
+ with %Activity{data: %{"object" => %{"likes" => likes}}} <- Activity.get_by_id(id) do
q = from(u in User, where: u.ap_id in ^likes)
users = Repo.all(q)
end
def reblogged_by(conn, %{"id" => id}) do
- with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do
+ with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Activity.get_by_id(id) do
q = from(u in User, where: u.ap_id in ^announces)
users = Repo.all(q)
get("/users/search", AdminAPIController, :search_users)
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)
+ post("/disable_account", UtilController, :disable_account)
end
scope [] do
end
end
+ def disable_account(%{assigns: %{user: user}} = conn, params) do
+ case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
+ {:ok, user} ->
+ User.disable_async(user)
+ json(conn, %{status: "success"})
+
+ {:error, msg} ->
+ json(conn, %{error: msg})
+ end
+ end
+
def captcha(conn, _params) do
json(conn, Pleroma.Captcha.new())
end
end
def delete(%User{} = user, id) do
- with %Activity{data: %{"type" => _type}} <- Repo.get(Activity, id),
+ with %Activity{data: %{"type" => _type}} <- Activity.get_by_id(id),
{:ok, activity} <- CommonAPI.delete(id, user) do
{:ok, activity}
end
def get_user(user \\ nil, params) do
case params do
%{"user_id" => user_id} ->
- case target = User.get_cached_by_nickname_or_id(user_id) do
+ case User.get_cached_by_nickname_or_id(user_id) do
nil ->
{:error, "No user with such user_id"}
- _ ->
- {:ok, target}
+ %User{info: %{disabled: true}} ->
+ {:error, "User has been disabled"}
+
+ user ->
+ {:ok, user}
end
%{"screen_name" => nickname} ->
- case target = Repo.get_by(User, nickname: nickname) do
+ case User.get_by_nickname(nickname) do
nil ->
{:error, "No user with such screen_name"}
- _ ->
- {:ok, target}
+ %User{info: %{disabled: true}} ->
+ {:error, "User has been disabled"}
+
+ user ->
+ {:ok, user}
end
_ ->
end
def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Repo.get(Activity, id),
+ with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.visible_for_user?(activity, user) do
conn
|> put_view(ActivityView)
end
def get_by_id_or_ap_id(id) do
- activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
+ activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id)
if activity.data["type"] == "Create" do
activity
--- /dev/null
+defmodule Pleroma.Repo.Migrations.UsersAddDisabledIndex do
+ use Ecto.Migration
+
+ def change do
+ create(index(:users, ["(info->'disabled')"], name: :users_disabled_index, using: :gin))
+ end
+end