mirror of
https://github.com/philomena-dev/philomena.git
synced 2025-01-19 14:17:59 +01:00
more admin tools
This commit is contained in:
parent
c45a2a16d9
commit
8b220775bb
15 changed files with 486 additions and 5 deletions
72
lib/philomena/user_downvote_wipe.ex
Normal file
72
lib/philomena/user_downvote_wipe.ex
Normal file
|
@ -0,0 +1,72 @@
|
|||
defmodule Philomena.UserDownvoteWipe do
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
alias Philomena.ImageVotes.ImageVote
|
||||
alias Philomena.ImageFaves.ImageFave
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
|
||||
def perform(user, upvotes_and_faves_too \\ false) do
|
||||
ImageVote
|
||||
|> where(user_id: ^user.id, up: false)
|
||||
|> in_batches(fn queryable, image_ids ->
|
||||
Repo.delete_all(where(queryable, [iv], iv.image_id in ^image_ids))
|
||||
Repo.update_all(where(Image, [i], i.id in ^image_ids), inc: [downvotes_count: -1, score: 1])
|
||||
Images.reindex_images(image_ids)
|
||||
|
||||
# Allow time for indexing to catch up
|
||||
:timer.sleep(:timer.seconds(10))
|
||||
end)
|
||||
|
||||
if upvotes_and_faves_too do
|
||||
ImageVote
|
||||
|> where(user_id: ^user.id, up: true)
|
||||
|> in_batches(fn queryable, image_ids ->
|
||||
Repo.delete_all(where(queryable, [iv], iv.image_id in ^image_ids))
|
||||
Repo.update_all(where(Image, [i], i.id in ^image_ids), inc: [upvotes_count: -1, score: -1])
|
||||
Images.reindex_images(image_ids)
|
||||
|
||||
:timer.sleep(:timer.seconds(10))
|
||||
end)
|
||||
|
||||
ImageFave
|
||||
|> where(user_id: ^user.id)
|
||||
|> in_batches(fn queryable, image_ids ->
|
||||
Repo.delete_all(where(queryable, [iv], iv.image_id in ^image_ids))
|
||||
Repo.update_all(where(Image, [i], i.id in ^image_ids), inc: [faves_count: -1])
|
||||
Images.reindex_images(image_ids)
|
||||
|
||||
:timer.sleep(:timer.seconds(10))
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
# todo: extract this to its own module somehow
|
||||
defp in_batches(queryable, mapper) do
|
||||
queryable = order_by(queryable, asc: :image_id)
|
||||
|
||||
ids =
|
||||
queryable
|
||||
|> select([q], q.image_id)
|
||||
|> limit(1000)
|
||||
|> Repo.all()
|
||||
|
||||
queryable
|
||||
|> in_batches(mapper, 1000, ids)
|
||||
end
|
||||
|
||||
defp in_batches(_queryable, _mapper, _batch_size, []), do: nil
|
||||
|
||||
defp in_batches(queryable, mapper, batch_size, ids) do
|
||||
mapper.(exclude(queryable, :order_by), ids)
|
||||
|
||||
ids =
|
||||
queryable
|
||||
|> where([q], q.image_id > ^Enum.max(ids))
|
||||
|> select([q], q.image_id)
|
||||
|> limit(^batch_size)
|
||||
|> Repo.all()
|
||||
|
||||
in_batches(queryable, mapper, batch_size, ids)
|
||||
end
|
||||
end
|
68
lib/philomena/user_wipe.ex
Normal file
68
lib/philomena/user_wipe.ex
Normal file
|
@ -0,0 +1,68 @@
|
|||
defmodule Philomena.UserWipe do
|
||||
@wipe_ip %Postgrex.INET{address: {127, 0, 1, 1}, netmask: 32}
|
||||
@wipe_fp "ffff"
|
||||
|
||||
alias Philomena.Comments.Comment
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Posts.Post
|
||||
alias Philomena.Reports.Report
|
||||
alias Philomena.SourceChanges.SourceChange
|
||||
alias Philomena.TagChanges.TagChange
|
||||
alias Philomena.UserIps.UserIp
|
||||
alias Philomena.UserFingerprints.UserFingerprint
|
||||
alias Philomena.Users.User
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
|
||||
def perform(user) do
|
||||
random_hex = :crypto.strong_rand_bytes(16) |> Base.encode16(case: :lower)
|
||||
|
||||
for schema <- [Comment, Image, Post, Report, SourceChange, TagChange] do
|
||||
schema
|
||||
|> where(user_id: ^user.id)
|
||||
|> in_batches(&Repo.update_all(&1, set: [ip: @wipe_ip, fingerprint: @wipe_fp]))
|
||||
end
|
||||
|
||||
UserIp
|
||||
|> where(user_id: ^user.id)
|
||||
|> Repo.delete_all()
|
||||
|
||||
UserFingerprint
|
||||
|> where(user_id: ^user.id)
|
||||
|> Repo.delete_all()
|
||||
|
||||
User
|
||||
|> where(id: ^user.id)
|
||||
|> Repo.update_all(set: [email: "deactivated#{random_hex}@example.com", current_sign_in_ip: @wipe_ip, last_sign_in_ip: @wipe_ip])
|
||||
end
|
||||
|
||||
defp in_batches(queryable, mapper) do
|
||||
queryable = order_by(queryable, asc: :id)
|
||||
|
||||
ids =
|
||||
queryable
|
||||
|> select([q], q.id)
|
||||
|> limit(1000)
|
||||
|> Repo.all()
|
||||
|
||||
in_batches(queryable, mapper, 1000, ids)
|
||||
end
|
||||
|
||||
defp in_batches(_queryable, _mapper, _batch_size, []), do: nil
|
||||
|
||||
defp in_batches(queryable, mapper, batch_size, ids) do
|
||||
queryable
|
||||
|> where([q], q.id in ^ids)
|
||||
|> exclude(:order_by)
|
||||
|> mapper.()
|
||||
|
||||
ids =
|
||||
queryable
|
||||
|> where([q], q.id > ^Enum.max(ids))
|
||||
|> select([q], q.id)
|
||||
|> limit(^batch_size)
|
||||
|> Repo.all()
|
||||
|
||||
in_batches(queryable, mapper, batch_size, ids)
|
||||
end
|
||||
end
|
|
@ -149,6 +149,24 @@ defmodule Philomena.Users do
|
|||
|> Repo.isolated_transaction(:serializable)
|
||||
end
|
||||
|
||||
def reactivate_user(%User{} = user) do
|
||||
user
|
||||
|> User.reactivate_changeset()
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def deactivate_user(moderator, %User{} = user) do
|
||||
user
|
||||
|> User.deactivate_changeset(moderator)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def reset_api_key(%User{} = user) do
|
||||
user
|
||||
|> User.api_key_changeset()
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking user changes.
|
||||
|
||||
|
|
|
@ -249,6 +249,20 @@ defmodule Philomena.Users.User do
|
|||
change(user, watched_tag_ids: watched_tag_ids)
|
||||
end
|
||||
|
||||
def reactivate_changeset(user) do
|
||||
change(user, deleted_at: nil, deleted_by_user_id: nil)
|
||||
end
|
||||
|
||||
def deactivate_changeset(user, moderator) do
|
||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||
|
||||
change(user, deleted_at: now, deleted_by_user_id: moderator.id)
|
||||
end
|
||||
|
||||
def api_key_changeset(user) do
|
||||
put_api_key(user)
|
||||
end
|
||||
|
||||
def create_totp_secret_changeset(user) do
|
||||
secret = :crypto.strong_rand_bytes(15) |> Base.encode32()
|
||||
data = Philomena.Users.Encryptor.encrypt_model(secret)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
defmodule PhilomenaWeb.Admin.User.ActivationController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Users.User
|
||||
alias Philomena.Users
|
||||
|
||||
plug :verify_authorized
|
||||
plug :load_resource, model: User, id_name: "user_id", id_field: "slug", persisted: true
|
||||
|
||||
def create(conn, _params) do
|
||||
{:ok, user} = Users.reactivate_user(conn.assigns.user)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "User was reactivated.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, user))
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, user} = Users.deactivate_user(conn.assigns.current_user, conn.assigns.user)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "User was deactivated.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, user))
|
||||
end
|
||||
|
||||
defp verify_authorized(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true -> conn
|
||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
defmodule PhilomenaWeb.Admin.User.ApiKeyController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Users.User
|
||||
alias Philomena.Users
|
||||
|
||||
plug :verify_authorized
|
||||
plug :load_resource, model: User, id_name: "user_id", id_field: "slug", persisted: true
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, user} = Users.reset_api_key(conn.assigns.user)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "API token successfully reset.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, user))
|
||||
end
|
||||
|
||||
defp verify_authorized(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true -> conn
|
||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
defmodule PhilomenaWeb.Admin.User.DownvoteController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.UserDownvoteWipe
|
||||
alias Philomena.Users.User
|
||||
|
||||
plug :verify_authorized
|
||||
plug :load_resource, model: User, id_name: "user_id", id_field: "slug", persisted: true
|
||||
|
||||
def delete(conn, _params) do
|
||||
spawn fn ->
|
||||
UserDownvoteWipe.perform(conn.assigns.user)
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Downvote wipe started.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user))
|
||||
end
|
||||
|
||||
defp verify_authorized(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true -> conn
|
||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||
end
|
||||
end
|
||||
end
|
26
lib/philomena_web/controllers/admin/user/vote_controller.ex
Normal file
26
lib/philomena_web/controllers/admin/user/vote_controller.ex
Normal file
|
@ -0,0 +1,26 @@
|
|||
defmodule PhilomenaWeb.Admin.User.VoteController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.UserDownvoteWipe
|
||||
alias Philomena.Users.User
|
||||
|
||||
plug :verify_authorized
|
||||
plug :load_resource, model: User, id_name: "user_id", id_field: "slug", persisted: true
|
||||
|
||||
def delete(conn, _params) do
|
||||
spawn fn ->
|
||||
UserDownvoteWipe.perform(conn.assigns.user, true)
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Vote and fave wipe started.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user))
|
||||
end
|
||||
|
||||
defp verify_authorized(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true -> conn
|
||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||
end
|
||||
end
|
||||
end
|
26
lib/philomena_web/controllers/admin/user/wipe_controller.ex
Normal file
26
lib/philomena_web/controllers/admin/user/wipe_controller.ex
Normal file
|
@ -0,0 +1,26 @@
|
|||
defmodule PhilomenaWeb.Admin.User.WipeController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.UserWipe
|
||||
alias Philomena.Users.User
|
||||
|
||||
plug :verify_authorized
|
||||
plug :load_resource, model: User, id_name: "user_id", id_field: "slug", persisted: true
|
||||
|
||||
def create(conn, _params) do
|
||||
spawn fn ->
|
||||
UserWipe.perform(conn.assigns.user)
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "PII wipe started, please verify and then deactivate the account as necessary.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user))
|
||||
end
|
||||
|
||||
defp verify_authorized(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true -> conn
|
||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,12 +11,15 @@ defmodule PhilomenaWeb.ProfileController do
|
|||
alias Philomena.Comments.Comment
|
||||
alias Philomena.Interactions
|
||||
alias Philomena.Tags.Tag
|
||||
alias Philomena.UserIps.UserIp
|
||||
alias Philomena.UserFingerprints.UserFingerprint
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
|
||||
plug :load_and_authorize_resource, model: User, only: :show, id_field: "slug", preload: [
|
||||
awards: :badge, public_links: :tag, verified_links: :tag, commission: [sheet_image: :tags, items: [example_image: :tags]]
|
||||
]
|
||||
plug :set_admin_metadata
|
||||
|
||||
def show(conn, _params) do
|
||||
current_user = conn.assigns.current_user
|
||||
|
@ -190,4 +193,33 @@ defmodule PhilomenaWeb.ProfileController do
|
|||
|
||||
images
|
||||
end
|
||||
|
||||
defp set_admin_metadata(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true ->
|
||||
user = Repo.preload(conn.assigns.user, :current_filter)
|
||||
filter = user.current_filter
|
||||
last_ip =
|
||||
UserIp
|
||||
|> where(user_id: ^user.id)
|
||||
|> order_by(desc: :updated_at)
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
|
||||
last_fp =
|
||||
UserFingerprint
|
||||
|> where(user_id: ^user.id)
|
||||
|> order_by(desc: :updated_at)
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
|
||||
conn
|
||||
|> assign(:filter, filter)
|
||||
|> assign(:last_ip, last_ip)
|
||||
|> assign(:last_fp, last_fp)
|
||||
|
||||
_false ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule PhilomenaWeb.ReloadUserPlug do
|
|||
alias Philomena.UserIps.UserIp
|
||||
alias Philomena.UserFingerprints.UserFingerprint
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
|
@ -33,20 +34,24 @@ defmodule PhilomenaWeb.ReloadUserPlug do
|
|||
fp = conn.cookies["_ses"]
|
||||
|
||||
if ip do
|
||||
update = update(UserIp, inc: [uses: 1], set: [updated_at: fragment("now()")])
|
||||
|
||||
Repo.insert_all(
|
||||
UserIp,
|
||||
[%{user_id: user.id, ip: ip, uses: 1}],
|
||||
conflict_target: [:user_id, :ip],
|
||||
on_conflict: [inc: [uses: 1]]
|
||||
on_conflict: update
|
||||
)
|
||||
end
|
||||
|
||||
if fp do
|
||||
update = update(UserFingerprint, inc: [uses: 1], set: [updated_at: fragment("now()")])
|
||||
|
||||
Repo.insert_all(
|
||||
UserFingerprint,
|
||||
[%{user_id: user.id, fingerprint: fp, uses: 1}],
|
||||
conflict_target: [:user_id, :fingerprint],
|
||||
on_conflict: [inc: [uses: 1]]
|
||||
on_conflict: update
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -224,11 +224,18 @@ defmodule PhilomenaWeb.Router do
|
|||
resources "/mod_notes", ModNoteController, except: [:show]
|
||||
resources "/users", UserController, only: [:index, :edit, :update] do
|
||||
resources "/avatar", User.AvatarController, only: [:delete], singleton: true
|
||||
resources "/activation", User.ActivationController, only: [:create, :delete], singleton: true
|
||||
resources "/api_key", User.ApiKeyController, only: [:delete], singleton: true
|
||||
resources "/downvotes", User.DownvoteController, only: [:delete], singleton: true
|
||||
resources "/votes", User.VoteController, only: [:delete], singleton: true
|
||||
resources "/wipe", User.WipeController, only: [:create], singleton: true
|
||||
end
|
||||
|
||||
resources "/batch/tags", Batch.TagController, only: [:update], singleton: true
|
||||
scope "/donations", Donation, as: :donation do
|
||||
resources "/user", UserController, only: [:show]
|
||||
end
|
||||
resources "/donations", DonationController, only: [:index, :create]
|
||||
resources "/donations/user", Donation.UserController, only: [:show]
|
||||
end
|
||||
|
||||
resources "/duplicate_reports", DuplicateReportController, only: [] do
|
||||
|
|
124
lib/philomena_web/templates/profile/_admin_block.html.slime
Normal file
124
lib/philomena_web/templates/profile/_admin_block.html.slime
Normal file
|
@ -0,0 +1,124 @@
|
|||
.block--fixed
|
||||
i.fa.fa-fw.fa-calendar>
|
||||
' Account created
|
||||
= @user.created_at
|
||||
|
||||
br
|
||||
i.fa.fa-fw.fa-filter>
|
||||
' Current Filter:
|
||||
= if @filter do
|
||||
= link @filter.name, to: Routes.filter_path(@conn, :show, @filter)
|
||||
- else
|
||||
em
|
||||
' (none)
|
||||
|
||||
br
|
||||
i.far.fa-fw.fa-clock>
|
||||
' Last seen
|
||||
= if @last_ip do
|
||||
=> pretty_time(@last_ip.updated_at)
|
||||
' from
|
||||
=> link_to_ip(@conn, @last_ip.ip)
|
||||
|
||||
= if @last_fp do
|
||||
=> link_to_fingerprint(@conn, @last_fp.fingerprint)
|
||||
- else
|
||||
em
|
||||
' (never)
|
||||
|
||||
br
|
||||
' Two factor auth:
|
||||
strong = enabled_text(@user.otp_required_for_login)
|
||||
|
||||
br
|
||||
|
||||
a.label.label--primary.label--block href="#" data-click-toggle=".js-admin__options__toggle" title="Toggle Controls"
|
||||
i.fa.fa-fw.fa-bars
|
||||
span Toggle Controls
|
||||
|
||||
.profile-top__options.js-admin__options__toggle.hidden
|
||||
ul.profile-admin__options__column
|
||||
li
|
||||
= link to: Routes.profile_detail_path(@conn, :index, @user) do
|
||||
i.fa.fa-fw.fa-eye
|
||||
span.admin__button View Details
|
||||
li
|
||||
= link to: Routes.search_path(@conn, :index, q: "upvoted_by_id:#{@user.id}") do
|
||||
i.fa.fa-fw.fa-arrow-up
|
||||
span.admin__button Upvotes
|
||||
li
|
||||
= link to: Routes.search_path(@conn, :index, q: "downvoted_by_id:#{@user.id}") do
|
||||
i.fa.fa-fw.fa-arrow-down
|
||||
span.admin__button Downvotes
|
||||
li
|
||||
= link to: Routes.search_path(@conn, :index, q: "hidden_by_id:#{@user.id}") do
|
||||
i.fa.fa-fw.fa-eye-slash
|
||||
span.admin__button Hidden Images
|
||||
li
|
||||
= link to: Routes.admin_report_path(@conn, :index, rq: "user_id:#{@user.id}") do
|
||||
i.fa.fa-fw.fa-exclamation
|
||||
span.admin__button Reports
|
||||
li
|
||||
= link to: Routes.profile_ip_history_path(@conn, :index, @user) do
|
||||
i.fab.fa-fw.fa-internet-explorer
|
||||
span.admin__button IP History
|
||||
li
|
||||
= link to: Routes.profile_fp_history_path(@conn, :index, @user) do
|
||||
i.fa.fa-fw.fa-desktop
|
||||
span.admin__button FP History
|
||||
li
|
||||
= link to: Routes.profile_alias_path(@conn, :index, @user) do
|
||||
i.fa.fa-fw.fa-users
|
||||
span.admin__button Potential Aliases
|
||||
|
||||
ul.profile-admin__options__column
|
||||
|
||||
li
|
||||
= link to: Routes.admin_user_path(@conn, :edit, @user) do
|
||||
i.fas.fa-fw.fa-edit
|
||||
span.admin__button Edit User
|
||||
|
||||
= if @user.deleted_at do
|
||||
li
|
||||
= link to: Routes.admin_user_activation_path(@conn, :create, @user), data: [confirm: "Are you really, really sure?", method: "post"] do
|
||||
i.fa.fa-fw.fa-check
|
||||
span.admin__button Reactivate Account
|
||||
- else
|
||||
li
|
||||
= link to: Routes.admin_user_activation_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do
|
||||
i.fa.fa-fw.fa-times
|
||||
span.admin__button Deactivate Account
|
||||
li
|
||||
= link to: Routes.admin_donation_user_path(@conn, :show, @user) do
|
||||
i.fas.fa-fw.fa-dollar-sign
|
||||
span.admin__button Donations
|
||||
|
||||
li
|
||||
= link to: Routes.profile_user_link_path(@conn, :new, @user) do
|
||||
i.fa.fa-fw.fa-link
|
||||
span.admin__button Add User Link
|
||||
|
||||
li
|
||||
= link to: Routes.admin_user_vote_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do
|
||||
i.far.fa-fw.fa-file-excel
|
||||
span.admin__button Remove All Votes/Faves
|
||||
|
||||
li
|
||||
= link to: Routes.admin_user_downvote_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do
|
||||
i.fa.fa-fw.fa-arrow-down
|
||||
span.admin__button Remove All Downvotes
|
||||
|
||||
li
|
||||
= link to: Routes.admin_user_ban_path(@conn, :new, username: @user.name) do
|
||||
i.fa.fa-fw.fa-ban
|
||||
span.admin__button Ban this sucker
|
||||
|
||||
li
|
||||
= link to: Routes.admin_user_wipe_path(@conn, :create, @user), data: [confirm: "This is irreversible, destroying all identifying information including email. Are you sure?", method: "post"] do
|
||||
i.fas.fa-fw.fa-eraser
|
||||
span.admin__button Wipe PII
|
||||
|
||||
li
|
||||
= link to: Routes.admin_user_api_key_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do
|
||||
i.fas.fa-fw.fa-key
|
||||
span.admin__button Reset API key
|
|
@ -22,8 +22,6 @@
|
|||
li = link("Send message", to: Routes.conversation_path(@conn, :new, recipient: @user.name))
|
||||
li = link("Our conversations", to: Routes.conversation_path(@conn, :index, with: @user.id))
|
||||
li = link("Report this user", to: Routes.profile_report_path(@conn, :new, @user))
|
||||
= if can_ban?(@conn) do
|
||||
li = link("Ban this sucker", to: Routes.admin_user_ban_path(@conn, :new, username: @user.name))
|
||||
|
||||
ul.profile-top__options__column
|
||||
li = link("Uploads", to: Routes.search_path(@conn, :index, q: "uploader_id:#{@user.id}"))
|
||||
|
@ -37,6 +35,9 @@
|
|||
li = link("Tag changes", to: Routes.profile_tag_change_path(@conn, :index, @user))
|
||||
li = link("Source changes", to: Routes.profile_source_change_path(@conn, :index, @user))
|
||||
|
||||
= if can_index_user?(@conn) do
|
||||
= render PhilomenaWeb.ProfileView, "_admin_block.html", assigns
|
||||
|
||||
= if (current?(@user, @conn.assigns.current_user) or can?(@conn, :index, UserBan)) and Enum.any?(@bans) do
|
||||
.block
|
||||
.block__header
|
||||
|
|
|
@ -56,6 +56,12 @@ defmodule PhilomenaWeb.ProfileView do
|
|||
def can_ban?(conn),
|
||||
do: can?(conn, :index, Philomena.Bans.User)
|
||||
|
||||
def can_index_user?(conn),
|
||||
do: can?(conn, :index, Philomena.Users.User)
|
||||
|
||||
def enabled_text(true), do: "Enabled"
|
||||
def enabled_text(_else), do: "Disabled"
|
||||
|
||||
def user_abbrv(conn, %{name: name} = user) do
|
||||
abbrv = String.upcase(initials_abbrv(name) || uppercase_abbrv(name) || first_letters_abbrv(name))
|
||||
abbrv = "(" <> abbrv <> ")"
|
||||
|
|
Loading…
Reference in a new issue