066765d2dc
* Add chat moderation rules to the website, so admins can apply selective rules to problematic users. Available rules are: * redcam: user's camera is always NSFW. * nobroadcast: user can not broadcast their camera. * novideo: user can not broadcast OR watch any video. * noimage: user can not share OR see any shared image on chat. * The page to manage a user's active rules is available on their admin card of their profile page. When the user has rules active, a yellow counter is shown by the link to manage their rules. * Only chat moderator admins have access to the page or can see the yellow counter to know whether rules are active. * "Shy Accounts" are now permitted on the chat room! With some moderation rules automatically applied to them: novideo,noimage. * Update the Shy Account FAQ and messaging on the chat landing page. * Update the auto-kick from chat behavior regarding shy accounts: * They are kicked from chat only when an update to their profile settings will transition then FROM a non-shy into a shy account. * For example: when saving their profile settings (going private) or when editing or deleting a photo (if they will have no more public photos left)
411 lines
24 KiB
HTML
411 lines
24 KiB
HTML
{{define "title"}}Admin Action: {{.User.Username}}{{end}}
|
|
{{define "content"}}
|
|
<div class="container">
|
|
<section class="hero is-link is-bold">
|
|
<div class="hero-body">
|
|
<div class="container">
|
|
<h1 class="title">
|
|
Admin Action
|
|
</h1>
|
|
<h2 class="subtitle">On user {{.User.Username}}</h2>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{$Root := .}}
|
|
|
|
<div class="block p-4">
|
|
<div class="columns is-centered">
|
|
<div class="column is-half">
|
|
|
|
<div class="card" style="width: 100%; max-width: 640px">
|
|
<header class="card-header has-background-link">
|
|
<p class="card-header-title has-text-light">
|
|
{{if eq .Intent "impersonate"}}
|
|
<i class="mr-2 fa fa-ghost"></i>
|
|
Impersonate User
|
|
{{else if eq .Intent "chat.rules"}}
|
|
<i class="mr-2 fa fa-gavel"></i>
|
|
Chat Moderation Rules
|
|
{{else if eq .Intent "essays"}}
|
|
<i class="mr-2 fa fa-pencil"></i>
|
|
Edit Profile Text
|
|
{{else if eq .Intent "ban"}}
|
|
<i class="mr-2 fa fa-ban"></i>
|
|
Ban User
|
|
{{else if eq .Intent "promote"}}
|
|
<i class="mr-2 fa fa-peace"></i>
|
|
Promote User
|
|
{{else if eq .Intent "password"}}
|
|
<i class="mr-2 fa fa-lock"></i>
|
|
Reset Password
|
|
{{else if eq .Intent "delete"}}
|
|
<i class="mr-2 fa fa-trash"></i>
|
|
Delete User
|
|
{{else if eq .Intent "insights"}}
|
|
<i class="mr-2 fa fa-search"></i>
|
|
Admin Insights
|
|
{{end}}
|
|
</p>
|
|
</header>
|
|
<div class="card-content">
|
|
|
|
<div class="media block">
|
|
<div class="media-left">
|
|
{{template "avatar-64x64" .User}}
|
|
</div>
|
|
<div class="media-content">
|
|
<p class="title is-4">{{.User.NameOrUsername}}</p>
|
|
<p class="subtitle is-6">
|
|
<span class="icon"><i class="fa fa-user"></i></span>
|
|
<a href="/u/{{.User.Username}}" target="_blank">{{.User.Username}}</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form action="/admin/user-action" method="POST">
|
|
{{InputCSRF}}
|
|
<input type="hidden" name="intent" value="{{.Intent}}">
|
|
<input type="hidden" name="user_id" value="{{.User.ID}}">
|
|
|
|
{{if eq .Intent "insights"}}
|
|
<div class="block content">
|
|
<h2>Admin Insights</h2>
|
|
|
|
<p>
|
|
This page gives a peek into the database to glean some insights about a user.
|
|
So far, this means taking a look at their block lists: how many people do they
|
|
block (and who), and more importantly, how many people are blocking them. It
|
|
may be useful information to guage a problematic user, if they are angering or
|
|
creeping a lot of people out and ending up on a lot of block lists.
|
|
</p>
|
|
|
|
<h3>Social Change Log</h3>
|
|
|
|
<p class="is-size-7">
|
|
These links go into the admin Change Log viewer to show history of the user's
|
|
interactions with parts of the website. Also look for "Change Log" buttons scattered
|
|
around various pages (profile, gallery, certification photo view) for history
|
|
about the user's data tables.
|
|
</p>
|
|
|
|
<ul>
|
|
<li>
|
|
<a href="/admin/changelog?table_name=comments&table_id={{.User.ID}}">
|
|
Comments written
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="/admin/changelog?table_name=threads&table_id={{.User.ID}}">
|
|
Forum threads started
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<h3>Block Lists</h3>
|
|
|
|
<!-- Surface if admin users are blocked -->
|
|
{{if .AdminBlockCount}}
|
|
<h5 class="has-text-danger">
|
|
<i class="fa fa-peace"></i>
|
|
Blocked Admins <span class="tag is-danger">{{.AdminBlockCount}}</span>
|
|
</h5>
|
|
|
|
<p>
|
|
This user blocks <strong>{{.AdminBlockCount}}</strong> out of {{.AdminBlockTotal}} admin{{Pluralize64 .AdminBlockTotal}} of this website.
|
|
</p>
|
|
|
|
<p>
|
|
If this number is unusually high, it can indicate this user may be proactively blocking all the admins in order to be
|
|
sneaky or evade moderation.
|
|
</p>
|
|
{{end}}
|
|
|
|
<div class="columns">
|
|
<div class="column">
|
|
<h5 class="has-text-warning">Forward List <span class="tag is-warning">{{len .BlocklistInsights.Blocks}}</span></h5>
|
|
|
|
<p>
|
|
(Users who {{.User.Username}} is blocking)
|
|
</p>
|
|
|
|
<p>
|
|
<a href="/admin/changelog?table_name=blocks&about_user_id={{.User.ID}}" class="button is-small">
|
|
<span class="icon"><i class="fa fa-clipboard-list"></i></span>
|
|
<span>Change Log</span>
|
|
</a>
|
|
</p>
|
|
|
|
<ul>
|
|
{{range .BlocklistInsights.Blocks}}
|
|
<li>
|
|
<a href="/u/{{.Username}}">{{.Username}}</a>
|
|
{{if .IsAdmin}}
|
|
<sup class="has-text-warning fa fa-peace" title="Admin user"></sup>
|
|
{{end}}
|
|
<small class="has-text-grey" title="{{.Date}}">{{.Date.Format "2006-01-02"}}</small>
|
|
</li>
|
|
{{end}}
|
|
</ul>
|
|
</div>
|
|
<div class="column">
|
|
<h5 class="has-text-warning">Reverse List <span class="tag is-danger">{{len .BlocklistInsights.BlockedBy}}</span></h5>
|
|
|
|
<p>
|
|
(Users who block {{.User.Username}})
|
|
</p>
|
|
|
|
<p>
|
|
<a href="/admin/changelog?table_name=blocks&table_id={{.User.ID}}" class="button is-small">
|
|
<span class="icon"><i class="fa fa-clipboard-list"></i></span>
|
|
<span>Change Log</span>
|
|
</a>
|
|
</p>
|
|
|
|
<ul>
|
|
{{range .BlocklistInsights.BlockedBy}}
|
|
<li>
|
|
<a href="/u/{{.Username}}">{{.Username}}</a>
|
|
{{if .IsAdmin}}
|
|
<sup class="has-text-warning fa fa-peace" title="Admin user"></sup>
|
|
{{end}}
|
|
<small class="has-text-grey" title="{{.Date}}">{{.Date.Format "2006-01-02"}}</small>
|
|
</li>
|
|
{{end}}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{else if eq .Intent "chat.rules"}}
|
|
<div class="block content">
|
|
<p>
|
|
You may use this page to add or remove <strong>chat moderation rules</strong> for this user account.
|
|
</p>
|
|
|
|
<p>
|
|
Moderation rules are useful to apply restrictions to certain problematic users who habitually break
|
|
the site rules. For example: somebody who insists on keeping their camera "blue" (non-explicit) while
|
|
always jerking off and resisting the admin request that their camera should be marked "red" can have that
|
|
choice taken away from them, and have their camera be forced red at all times when they are broadcasting.
|
|
</p>
|
|
|
|
<p>
|
|
<strong>Note:</strong> <a href="/faq#shy-faqs">"Shy Accounts"</a> automatically have the <strong>No webcam privileges</strong>
|
|
and <strong>No image sharing privileges</strong> rules applied when they log onto the chat room.
|
|
</p>
|
|
</div>
|
|
|
|
{{range .ChatModerationRules}}
|
|
<div class="field">
|
|
<label class="checkbox">
|
|
<input type="checkbox"
|
|
name="rules"
|
|
value="{{.Value}}"
|
|
{{if $Root.User.ProfileFieldIn "chat_moderation_rules" .Value}}checked{{end}}>
|
|
{{.Label}}
|
|
</label>
|
|
<p class="help">
|
|
{{.Help}}
|
|
</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit" class="button is-success">
|
|
Save Changes
|
|
</button>
|
|
</div>
|
|
{{else if eq .Intent "essays"}}
|
|
<div class="block content">
|
|
<p>
|
|
You may use this page to edit the essay texts (e.g. About Me) section of a user's profile page.
|
|
The main use cases may be to remove Onlyfans spammy links or that sort of thing, so that you
|
|
don't need to fully impersonate their account to do so.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label" for="about_me">About Me</label>
|
|
<textarea class="textarea mb-4"
|
|
cols="80" rows="4"
|
|
name="about_me" id="about_me">{{.User.GetProfileField "about_me"}}</textarea>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label" for="interests">My Interests</label>
|
|
<textarea class="textarea mb-4"
|
|
cols="80" rows="4"
|
|
name="interests" id="interests">{{.User.GetProfileField "interests"}}</textarea>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label" for="music_movies">Music/Bands/Movies</label>
|
|
<textarea class="textarea mb-4"
|
|
cols="80" rows="4"
|
|
name="music_movies" id="music_movies">{{.User.GetProfileField "music_movies"}}</textarea>
|
|
</div>
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit" class="button is-success">
|
|
Save Changes
|
|
</button>
|
|
</div>
|
|
{{else if eq .Intent "impersonate"}}
|
|
<div class="block content">
|
|
<h3>With great power...</h3>
|
|
<p>
|
|
By <strong>impersonating</strong> this user, you will be considered as "logged in"
|
|
to their account and have access to their messages, profile, photos and settings.
|
|
</p>
|
|
<p>
|
|
Please respect user privacy and only impersonate an account as needed to diagnose
|
|
a customer support issue or similar.
|
|
</p>
|
|
<p>
|
|
<strong class="has-text-danger">
|
|
This event is logged and will be noticed.
|
|
</strong>
|
|
Write an explanation below why you are impersonating this user. It will
|
|
be e-mailed to the admin mailing list and trigger an admin notification
|
|
and be logged as a <a href="/admin/feedback?intent=report">Report</a> to
|
|
the admin dashboard. Reports can be acknowledged, but not deleted.
|
|
</p>
|
|
<p>
|
|
Good reasons may include:
|
|
<ul>
|
|
<li>
|
|
I need to diagnose a bug report given by one of our users
|
|
(briefly describe what the bug is; e.g. user saw a database error
|
|
at the top of a page).
|
|
</li>
|
|
<li>
|
|
A user has reported a Direct Message conversation and I need to
|
|
take a look at the context. (There is no other way to read user DMs)
|
|
</li>
|
|
</ul>
|
|
</p>
|
|
</div>
|
|
|
|
<textarea class="textarea mb-4"
|
|
cols="80" rows="4"
|
|
name="reason"
|
|
placeholder="Reason"
|
|
required></textarea>
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit" class="button is-success">
|
|
Log in as {{.User.Username}}
|
|
</button>
|
|
</div>
|
|
{{else if eq .Intent "ban"}}
|
|
<div class="block content">
|
|
<p>
|
|
This user is currently:
|
|
{{if eq .User.Status "active"}}
|
|
<strong class="has-text-success">Active (not banned)</strong>
|
|
{{else if eq .User.Status "disabled"}}
|
|
<strong class="has-text-warning">Disabled</strong>
|
|
{{else if eq .User.Status "banned"}}
|
|
<strong class="has-text-danger">Banned</strong>
|
|
{{end}}
|
|
</p>
|
|
|
|
<p>
|
|
Select a new status for them below:
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit" name="status" value="active" class="button is-success">
|
|
Active
|
|
</button>
|
|
<button type="submit" name="status" value="banned" class="button is-danger">
|
|
Banned
|
|
</button>
|
|
</div>
|
|
{{else if eq .Intent "promote"}}
|
|
<div class="block content">
|
|
<p>
|
|
This user is currently:
|
|
{{if .User.IsAdmin}}
|
|
<strong class="has-text-danger">Admin</strong>
|
|
{{else}}
|
|
<strong class="has-text-success">NOT Admin</strong>
|
|
{{end}}
|
|
</p>
|
|
|
|
<p>
|
|
Select a new status for them below:
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit" name="action" value="promote" class="button is-success">
|
|
Make Admin
|
|
</button>
|
|
<button type="submit" name="action" value="demote" class="button is-danger">
|
|
Remove Admin
|
|
</button>
|
|
</div>
|
|
{{else if eq .Intent "password"}}
|
|
<p class="block">
|
|
This page allows you to reset a user's password on their behalf. For example, if
|
|
they have forgotten their password and aren't receiving the e-mail reset link and
|
|
they reached out for manual assistance.
|
|
</p>
|
|
|
|
<div class="field">
|
|
<label class="label" for="password">New password:</label>
|
|
<input type="text" class="input" name="password" id="password" placeholder="Password">
|
|
<a href="#" id="random-password" class="has-text-warning is-size-7">
|
|
<i class="fa fa-refresh mr-2"></i> Random password
|
|
</a>
|
|
</div>
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit" name="action" value="password" class="button is-danger">
|
|
Update Password
|
|
</button>
|
|
<a href="/u/{{.User.Username}}" class="button is-success">Cancel</a>
|
|
</div>
|
|
|
|
<script>
|
|
window.addEventListener("DOMContentLoaded", (event) => {
|
|
// Password randomization.
|
|
const $password = document.querySelector("#password"),
|
|
$randomize = document.querySelector("#random-password");
|
|
alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789";
|
|
$randomize.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
let password = "";
|
|
for (let i = 0; i < 16; i++) {
|
|
password += alphabet[parseInt(Math.random() * alphabet.length)];
|
|
}
|
|
$password.value = password;
|
|
});
|
|
});
|
|
</script>
|
|
{{else if eq .Intent "delete"}}
|
|
<div class="block content">
|
|
<p>
|
|
Click the button below to <strong>deep delete</strong> this user account.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit" class="button is-danger">
|
|
Delete User Account
|
|
</button>
|
|
</div>
|
|
{{end}}
|
|
</form>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
{{end}} |