mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
Merge pull request 'main' (#3) from rimu/pyfedi:main into main
Reviewed-on: https://codeberg.org/hono4kami/pyfedi/pulls/3
This commit is contained in:
commit
6664550334
11 changed files with 210 additions and 152 deletions
|
@ -1147,60 +1147,30 @@ def admin_users():
|
||||||
page = request.args.get('page', 1, type=int)
|
page = request.args.get('page', 1, type=int)
|
||||||
search = request.args.get('search', '')
|
search = request.args.get('search', '')
|
||||||
local_remote = request.args.get('local_remote', '')
|
local_remote = request.args.get('local_remote', '')
|
||||||
|
sort_by = request.args.get('sort_by', 'last_seen DESC')
|
||||||
|
last_seen = request.args.get('last_seen', 0, type=int)
|
||||||
|
|
||||||
|
sort_by_btn = request.args.get('sort_by_btn', '')
|
||||||
|
if sort_by_btn:
|
||||||
|
return redirect(url_for('admin.admin_users', page=page, search=search, local_remote=local_remote, sort_by=sort_by_btn, last_seen=last_seen))
|
||||||
|
|
||||||
users = User.query.filter_by(deleted=False)
|
users = User.query.filter_by(deleted=False)
|
||||||
if local_remote == 'local':
|
if local_remote == 'local':
|
||||||
users = users.filter_by(ap_id=None)
|
users = users.filter_by(ap_id=None)
|
||||||
if local_remote == 'remote':
|
elif local_remote == 'remote':
|
||||||
users = users.filter(User.ap_id != None)
|
users = users.filter(User.ap_id != None)
|
||||||
if search:
|
if search:
|
||||||
users = users.filter(User.email.ilike(f"%{search}%"))
|
users = users.filter(User.email.ilike(f"%{search}%"))
|
||||||
users = users.order_by(User.user_name).paginate(page=page, per_page=1000, error_out=False)
|
if last_seen > 0:
|
||||||
|
users = users.filter(User.last_seen > utcnow() - timedelta(days=last_seen))
|
||||||
|
users = users.order_by(text('"user".' + sort_by))
|
||||||
|
users = users.paginate(page=page, per_page=1000, error_out=False)
|
||||||
|
|
||||||
next_url = url_for('admin.admin_users', page=users.next_num) if users.has_next else None
|
next_url = url_for('admin.admin_users', page=users.next_num, search=search, local_remote=local_remote, sort_by=sort_by, last_seen=last_seen) if users.has_next else None
|
||||||
prev_url = url_for('admin.admin_users', page=users.prev_num) if users.has_prev and page != 1 else None
|
prev_url = url_for('admin.admin_users', page=users.prev_num, search=search, local_remote=local_remote, sort_by=sort_by, last_seen=last_seen) if users.has_prev and page != 1 else None
|
||||||
|
|
||||||
return render_template('admin/users.html', title=_('Users'), next_url=next_url, prev_url=prev_url, users=users,
|
return render_template('admin/users.html', title=_('Users'), next_url=next_url, prev_url=prev_url, users=users,
|
||||||
local_remote=local_remote, search=search,
|
local_remote=local_remote, search=search, sort_by=sort_by, last_seen=last_seen,
|
||||||
moderating_communities=moderating_communities(current_user.get_id()),
|
|
||||||
joined_communities=joined_communities(current_user.get_id()),
|
|
||||||
menu_topics=menu_topics(),
|
|
||||||
site=g.site
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/users/trash', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@permission_required('administer all users')
|
|
||||||
def admin_users_trash():
|
|
||||||
|
|
||||||
page = request.args.get('page', 1, type=int)
|
|
||||||
search = request.args.get('search', '')
|
|
||||||
local_remote = request.args.get('local_remote', '')
|
|
||||||
type = request.args.get('type', 'bad_rep')
|
|
||||||
|
|
||||||
users = User.query.filter_by(deleted=False)
|
|
||||||
if local_remote == 'local':
|
|
||||||
users = users.filter_by(ap_id=None)
|
|
||||||
if local_remote == 'remote':
|
|
||||||
users = users.filter(User.ap_id != None)
|
|
||||||
if search:
|
|
||||||
users = users.filter(User.email.ilike(f"%{search}%"))
|
|
||||||
|
|
||||||
if type == '' or type == 'bad_rep':
|
|
||||||
users = users.filter(User.last_seen > utcnow() - timedelta(days=7))
|
|
||||||
users = users.filter(User.reputation < -10)
|
|
||||||
users = users.order_by(User.reputation).paginate(page=page, per_page=1000, error_out=False)
|
|
||||||
elif type == 'bad_attitude':
|
|
||||||
users = users.filter(User.last_seen > utcnow() - timedelta(days=7))
|
|
||||||
users = users.filter(User.attitude < 0.0).filter(User.reputation < -10)
|
|
||||||
users = users.order_by(User.attitude).paginate(page=page, per_page=1000, error_out=False)
|
|
||||||
|
|
||||||
next_url = url_for('admin.admin_users_trash', page=users.next_num, search=search, local_remote=local_remote, type=type) if users.has_next else None
|
|
||||||
prev_url = url_for('admin.admin_users_trash', page=users.prev_num, search=search, local_remote=local_remote, type=type) if users.has_prev and page != 1 else None
|
|
||||||
|
|
||||||
return render_template('admin/users_trash.html', title=_('Problematic users'), next_url=next_url, prev_url=prev_url, users=users,
|
|
||||||
local_remote=local_remote, search=search, type=type,
|
|
||||||
moderating_communities=moderating_communities(current_user.get_id()),
|
moderating_communities=moderating_communities(current_user.get_id()),
|
||||||
joined_communities=joined_communities(current_user.get_id()),
|
joined_communities=joined_communities(current_user.get_id()),
|
||||||
menu_topics=menu_topics(),
|
menu_topics=menu_topics(),
|
||||||
|
|
|
@ -710,6 +710,7 @@ class User(UserMixin, db.Model):
|
||||||
cover = db.relationship('File', lazy='joined', foreign_keys=[cover_id], single_parent=True, cascade="all, delete-orphan")
|
cover = db.relationship('File', lazy='joined', foreign_keys=[cover_id], single_parent=True, cascade="all, delete-orphan")
|
||||||
instance = db.relationship('Instance', lazy='joined', foreign_keys=[instance_id])
|
instance = db.relationship('Instance', lazy='joined', foreign_keys=[instance_id])
|
||||||
conversations = db.relationship('Conversation', lazy='dynamic', secondary=conversation_member, backref=db.backref('members', lazy='joined'))
|
conversations = db.relationship('Conversation', lazy='dynamic', secondary=conversation_member, backref=db.backref('members', lazy='joined'))
|
||||||
|
user_notes = db.relationship('UserNote', lazy='dynamic', foreign_keys="UserNote.target_id")
|
||||||
|
|
||||||
ap_id = db.Column(db.String(255), index=True) # e.g. username@server
|
ap_id = db.Column(db.String(255), index=True) # e.g. username@server
|
||||||
ap_profile_id = db.Column(db.String(255), index=True, unique=True) # e.g. https://server/u/username
|
ap_profile_id = db.Column(db.String(255), index=True, unique=True) # e.g. https://server/u/username
|
||||||
|
@ -1022,6 +1023,7 @@ class User(UserMixin, db.Model):
|
||||||
db.session.query(PostBookmark).filter(PostBookmark.user_id == self.id).delete()
|
db.session.query(PostBookmark).filter(PostBookmark.user_id == self.id).delete()
|
||||||
db.session.query(PostReplyBookmark).filter(PostReplyBookmark.user_id == self.id).delete()
|
db.session.query(PostReplyBookmark).filter(PostReplyBookmark.user_id == self.id).delete()
|
||||||
db.session.query(ModLog).filter(ModLog.user_id == self.id).delete()
|
db.session.query(ModLog).filter(ModLog.user_id == self.id).delete()
|
||||||
|
db.session.query(UserNote).filter(or_(UserNote.user_id == self.id, UserNote.target_id == self.id)).delete()
|
||||||
|
|
||||||
def purge_content(self, soft=True):
|
def purge_content(self, soft=True):
|
||||||
files = File.query.join(Post).filter(Post.user_id == self.id).all()
|
files = File.query.join(Post).filter(Post.user_id == self.id).all()
|
||||||
|
@ -1078,6 +1080,13 @@ class User(UserMixin, db.Model):
|
||||||
def has_read_post(self, post):
|
def has_read_post(self, post):
|
||||||
return self.read_post.filter(read_posts.c.read_post_id == post.id).count() > 0
|
return self.read_post.filter(read_posts.c.read_post_id == post.id).count() > 0
|
||||||
|
|
||||||
|
@cache.memoize(timeout=500)
|
||||||
|
def get_note(self, by_user):
|
||||||
|
user_note = self.user_notes.filter(UserNote.target_id == self.id, UserNote.user_id == by_user.id).first()
|
||||||
|
if user_note:
|
||||||
|
return user_note.body
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class ActivityLog(db.Model):
|
class ActivityLog(db.Model):
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<a href="{{ url_for('admin.admin_communities') }}">{{ _('Communities') }}</a> |
|
<a href="{{ url_for('admin.admin_communities') }}">{{ _('Communities') }}</a> |
|
||||||
<a href="{{ url_for('admin.admin_topics') }}">{{ _('Topics') }}</a> |
|
<a href="{{ url_for('admin.admin_topics') }}">{{ _('Topics') }}</a> |
|
||||||
<a href="{{ url_for('admin.admin_users', local_remote='local') }}">{{ _('Users') }}</a> |
|
<a href="{{ url_for('admin.admin_users', local_remote='local') }}">{{ _('Users') }}</a> |
|
||||||
<a href="{{ url_for('admin.admin_users_trash', local_remote='local') }}">{{ _('Watch') }}</a> |
|
|
||||||
{% if site.registration_mode == 'RequireApplication' %}
|
{% if site.registration_mode == 'RequireApplication' %}
|
||||||
<a href="{{ url_for('admin.admin_approve_registrations') }}">{{ _('Registration applications') }}</a> |
|
<a href="{{ url_for('admin.admin_approve_registrations') }}">{{ _('Registration applications') }}</a> |
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -11,44 +11,84 @@
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h1>{{ _('Users') }}</h1>
|
<h1>{{ _('Users') }}</h1>
|
||||||
<a class="btn btn-primary" href="{{ url_for('admin.admin_users_add') }}" style="float: right;">{{ _('Add local user') }}</a>
|
<a class="btn btn-primary" href="{{ url_for('admin.admin_users_add') }}" style="float: right;">{{ _('Add local user') }}</a>
|
||||||
<form method="get">
|
<form id="searchUsers" method="get">
|
||||||
<input type="search" name="search" value="{{ search }}">
|
<div>
|
||||||
<input type="radio" name="local_remote" value="local" id="local_remote_local" {{ 'checked' if local_remote == 'local' }}><label for="local_remote_local"> Local</label>
|
<input type="search" name="search" placeholder="{{ _('Search') }}" value="{{ search }}">
|
||||||
<input type="radio" name="local_remote" value="remote" id="local_remote_remote" {{ 'checked' if local_remote == 'remote' }}><label for="local_remote_remote"> Remote</label>
|
<input type="submit" name="submit_search" value="{{ _('Search') }}" class="btn btn-primary">
|
||||||
<input type="submit" name="submit" value="Search" class="btn btn-primary">
|
</div>
|
||||||
|
<div style="display:inline;">
|
||||||
|
<label for="local_remote">Local/Remote: </label>
|
||||||
|
<select name="local_remote" class="form-control-sm submit_on_change">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="local" {{ 'selected' if local_remote == 'local' }}>Local</option>
|
||||||
|
<option value="remote" {{ 'selected' if local_remote == 'remote' }}>Remote</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div style="display:inline;">
|
||||||
|
<label for="last_seen">Active: </label>
|
||||||
|
<select name="last_seen" class="form-control-sm submit_on_change">
|
||||||
|
<option value="0">All</option>
|
||||||
|
<option value="7" {{ 'selected' if last_seen == 7 }}>7 days</option>
|
||||||
|
<option value="30" {{ 'selected' if last_seen == 30 }}>30 days</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="sort_by" value="{{ sort_by }}"></button>
|
||||||
</form>
|
</form>
|
||||||
<table class="table table-striped mt-1">
|
<table class="table table-striped mt-1">
|
||||||
<tr>
|
<tr>
|
||||||
<th title="{{ _('Display name.') }}">{{ _('Name') }}</th>
|
<th>
|
||||||
<th title="{{ _('Last seen.') }}">{{ _('Seen') }}</th>
|
<button form="searchUsers" name="sort_by_btn" value="user_name{{' DESC' if sort_by == 'user_name ASC' else ' ASC' }}" class="btn" title="{{ _('Display name.') }}">
|
||||||
<th title="{{ _('Attitude: Percentage of up votes vs. down votes the account made.') }}">{{ _('Attitude') }}</th>
|
{{ _('Name') }}
|
||||||
<th title="{{ _('Reputation: The Karma of the account. Total up votes minus down votes they got.') }}">{{ _('Reputation') }}</th>
|
<span class="{{ 'fe fe-chevron-up' if sort_by == 'user_name DESC' }}{{ 'fe fe-chevron-down' if sort_by == 'user_name ASC' }}"></span>
|
||||||
|
</button>
|
||||||
|
</th>
|
||||||
<th>{{ _('Banned') }}</th>
|
<th>{{ _('Banned') }}</th>
|
||||||
<th title="{{ _('How often a user has been reported.') }}">{{ _('Reports') }}</th>
|
<th>
|
||||||
<th title="{{ _('IP address of last interaction.') }}">{{ _('IP and country code') }}</th>
|
<button form="searchUsers" name="sort_by_btn" value="reports{{' ASC' if sort_by == 'reports DESC' else ' DESC' }}" class="btn" title="{{ _('How often a user has been reported.') }}">
|
||||||
<th title="{{ _('Which website linked to PieFed when the user initially registered.') }}">{{ _('Source') }}</th>
|
{{ _('Reports') }}
|
||||||
|
<span class="{{ 'fe fe-chevron-up' if sort_by == 'reports ASC' }}{{ 'fe fe-chevron-down' if sort_by == 'reports DESC' }}"></span>
|
||||||
|
</button>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<button form="searchUsers" name="sort_by_btn" value="attitude{{' ASC' if sort_by == 'attitude DESC' else ' DESC' }}" class="btn" title="{{ _('Attitude: Percentage of up votes vs. down votes the account made.') }}">
|
||||||
|
{{ _('Attitude') }}
|
||||||
|
<span class="{{ 'fe fe-chevron-up' if sort_by == 'attitude ASC' }}{{ 'fe fe-chevron-down' if sort_by == 'attitude DESC' }}"></span>
|
||||||
|
</button>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<button form="searchUsers" name="sort_by_btn" value="reputation{{' ASC' if sort_by == 'reputation DESC' else ' DESC' }}" class="btn" title="{{ _('Reputation: The Karma of the account. Total up votes minus down votes they got.') }}">
|
||||||
|
{{ _('Reputation') }}
|
||||||
|
<span class="{{ 'fe fe-chevron-up' if sort_by == 'reputation ASC' }}{{ 'fe fe-chevron-down' if sort_by == 'reputation DESC' }}"></span>
|
||||||
|
</button>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<button form="searchUsers" name="sort_by_btn" value="last_seen{{' ASC' if sort_by == 'last_seen DESC' else ' DESC' }}" class="btn" title="{{ _('Last seen.') }}">
|
||||||
|
{{ _('Seen') }}
|
||||||
|
<span class="{{ 'fe fe-chevron-up' if sort_by == 'last_seen ASC' }}{{ 'fe fe-chevron-down' if sort_by == 'last_seen DESC' }}"></span>
|
||||||
|
</button>
|
||||||
|
</th>
|
||||||
<th>{{ _('Actions') }}</th>
|
<th>{{ _('Actions') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for user in users.items %}
|
{% for user in users.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ render_username(user, add_domain=False) }}<br />
|
<td>{{ render_username(user, add_domain=False) }}<br />
|
||||||
<a href="/u/{{ user.link() }}">{{ user.user_name }}</a>{% if not user.is_local() %}<wbr />@<a href="{{ user.ap_profile_id }}">{{ user.ap_domain }}</a>{% endif %}</td>
|
<a href="/u/{{ user.link() }}">{{ user.user_name }}</a>{% if not user.is_local() %}<wbr />@<a href="{{ user.ap_profile_id }}">{{ user.ap_domain }}</a>{% endif %}</td>
|
||||||
<td>{% if request.args.get('local_remote', '') == 'local' %}
|
|
||||||
{{ arrow.get(user.last_seen).humanize(locale=locale) }}
|
|
||||||
{% else %}
|
|
||||||
{{ user.last_seen }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{% if user.attitude != 1 %}{{ (user.attitude * 100) | round | int }}%{% endif %}</td>
|
|
||||||
<td>{% if user.reputation %}R {{ user.reputation | round | int }}{% endif %}</td>
|
|
||||||
<td>{{ '<span class="red">Banned</span>'|safe if user.banned }}
|
<td>{{ '<span class="red">Banned</span>'|safe if user.banned }}
|
||||||
{{ '<span class="red">Banned posts</span>'|safe if user.ban_posts }}
|
{{ '<span class="red">Banned posts</span>'|safe if user.ban_posts }}
|
||||||
{{ '<span class="red">Banned comments</span>'|safe if user.ban_comments }}</td>
|
{{ '<span class="red">Banned comments</span>'|safe if user.ban_comments }}</td>
|
||||||
<td>{{ user.reports if user.reports > 0 }} </td>
|
<td>{{ user.reports if user.reports > 0 }} </td>
|
||||||
<td>{{ user.ip_address if user.ip_address }}<br />{{ user.ip_address_country if user.ip_address_country }}</td>
|
<td>{% if user.attitude != 1 %}{{ (user.attitude * 100) | round | int }}%{% endif %}</td>
|
||||||
<td>{{ user.referrer if user.referrer }} </td>
|
<td>{% if user.reputation %}R {{ user.reputation | round | int }}{% endif %}</td>
|
||||||
<td><a href="{{ url_for('admin.admin_user_edit', user_id=user.id) }}">Edit</a> |
|
<td><span title="{{ user.last_seen }}">{{ arrow.get(user.last_seen).humanize(locale=locale) }}</span></td>
|
||||||
<a href="{{ url_for('admin.admin_user_delete', user_id=user.id) }}" class="confirm_first">Delete</a>
|
<td><a href="{{ url_for('admin.admin_user_edit', user_id=user.id) }}">Edit</a>,
|
||||||
|
<a href="{{ url_for('admin.admin_user_delete', user_id=user.id) }}" class="confirm_first">Delete</a>,
|
||||||
|
<br />
|
||||||
|
{% if user.banned %}
|
||||||
|
<a href="{{ url_for('user.unban_profile', actor=user.link()) }}" class="confirm_first">Ban</a>,
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('user.ban_profile', actor=user.link()) }}" class="confirm_first">Ban</a>,
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('user.ban_purge_profile', actor=user.link()) }}" class="confirm_first">Purge</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
|
|
||||||
{% extends 'themes/' + theme() + '/base.html' %}
|
|
||||||
{% else %}
|
|
||||||
{% extends "base.html" %}
|
|
||||||
{% endif %}
|
|
||||||
{% from 'bootstrap/form.html' import render_form %}
|
|
||||||
{% set active_child = 'admin_users_trash' %}
|
|
||||||
|
|
||||||
{% block app_content %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<h1>{{ _('Users') }}</h1>
|
|
||||||
<a class="btn btn-primary" href="{{ url_for('admin.admin_users_add') }}" style="float: right;">{{ _('Add local user') }}</a>
|
|
||||||
<form method="get">
|
|
||||||
<input type="search" name="search" value="{{ search }}">
|
|
||||||
<input type="radio" name="local_remote" value="local" id="local_remote_local" {{ 'checked' if local_remote == 'local' }}><label for="local_remote_local"> Local</label>
|
|
||||||
<input type="radio" name="local_remote" value="remote" id="local_remote_remote" {{ 'checked' if local_remote == 'remote' }}><label for="local_remote_remote"> Remote</label>
|
|
||||||
<input type="radio" name="type" value="bad_rep" id="type_bad_rep" {{ 'checked' if type == 'bad_rep' }}><label for="type_bad_rep"> Bad rep</label>
|
|
||||||
<input type="radio" name="type" value="bad_attitude" id="type_bad_attitude" {{ 'checked' if type == 'bad_attitude' }}><label for="type_bad_attitude"> Bad attitude</label>
|
|
||||||
<input type="submit" name="submit" value="Search" class="btn btn-primary">
|
|
||||||
</form>
|
|
||||||
<table class="table table-striped mt-1">
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Seen</th>
|
|
||||||
<th>Attitude</th>
|
|
||||||
<th>Rep</th>
|
|
||||||
<th>Banned</th>
|
|
||||||
<th>Reports</th>
|
|
||||||
<th>IP</th>
|
|
||||||
<th>Source</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
{% for user in users.items %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ render_username(user, add_domain=False) }}<br />
|
|
||||||
<a href="/u/{{ user.link() }}">{{ user.user_name }}</a>{% if not user.is_local() %}<wbr />@<a href="{{ user.ap_profile_id }}">{{ user.ap_domain }}</a>{% endif %}</td>
|
|
||||||
<td>{% if request.args.get('local_remote', '') == 'local' %}
|
|
||||||
{{ arrow.get(user.last_seen).humanize(locale=locale) }}
|
|
||||||
{% else %}
|
|
||||||
{{ user.last_seen }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{% if user.attitude != 1 %}{{ (user.attitude * 100) | round | int }}%{% endif %}</td>
|
|
||||||
<td>{% if user.reputation %}R {{ user.reputation | round | int }}{% endif %}</td>
|
|
||||||
<td>{{ '<span class="red">Banned</span>'|safe if user.banned }} </td>
|
|
||||||
<td>{{ user.reports if user.reports > 0 }} </td>
|
|
||||||
<td>{{ user.ip_address if user.ip_address }} </td>
|
|
||||||
<td>{{ user.referrer if user.referrer }} </td>
|
|
||||||
<td><a href="{{ url_for('admin.admin_user_edit', user_id=user.id) }}">Edit</a> |
|
|
||||||
<a href="{{ url_for('admin.admin_user_delete', user_id=user.id) }}" class="confirm_first">Delete</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<nav aria-label="Pagination" class="mt-4" role="navigation">
|
|
||||||
{% if prev_url %}
|
|
||||||
<a href="{{ prev_url }}" class="btn btn-primary">
|
|
||||||
<span aria-hidden="true">←</span> {{ _('Previous page') }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if next_url %}
|
|
||||||
<a href="{{ next_url }}" class="btn btn-primary">
|
|
||||||
{{ _('Next page') }} <span aria-hidden="true">→</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
{% include 'admin/_nav.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
{% endblock %}
|
|
|
@ -19,7 +19,9 @@
|
||||||
<div class="card-title text-center">{{ _('Create new account') }}</div>
|
<div class="card-title text-center">{{ _('Create new account') }}</div>
|
||||||
{{ render_form(form) }}
|
{{ render_form(form) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ _('Registration is closed. Only admins can create accounts.') }}
|
<p>{{ _('Registration is closed. Only admins can create accounts.') }}</p>
|
||||||
|
<p>{{ _('If you would like to sign up for PieFed, choose one of the other instances in our network:') }}</p>
|
||||||
|
<p class="text-center"><a class="btn btn-primary" href="https://join.piefed.social/try/" title="{{ _('List of open PieFed instances') }}">{{ _('Try PieFed') }}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,6 +27,12 @@
|
||||||
<span class="fe fe-warning orangered" title="Low reputation."> </span>
|
<span class="fe fe-warning orangered" title="Low reputation."> </span>
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
{% if current_user.is_authenticated -%}
|
||||||
|
{% set user_note = user.get_note(current_user) %}
|
||||||
|
{% if user_note -%}
|
||||||
|
<span class="user_note" title="{{ _('User note: %(note)s', note=user_note) }}">[{{ user_note | truncate(12, True) }}]</span>
|
||||||
|
{% endif -%}
|
||||||
|
{% endif -%}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
</span>
|
</span>
|
||||||
{% endmacro -%}
|
{% endmacro -%}
|
||||||
|
@ -217,7 +223,6 @@
|
||||||
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_communities' }}" href="{{ url_for('admin.admin_communities') }}">{{ _('Communities') }}</a></li>
|
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_communities' }}" href="{{ url_for('admin.admin_communities') }}">{{ _('Communities') }}</a></li>
|
||||||
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_topics' }}" href="{{ url_for('admin.admin_topics') }}">{{ _('Topics') }}</a></li>
|
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_topics' }}" href="{{ url_for('admin.admin_topics') }}">{{ _('Topics') }}</a></li>
|
||||||
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_users' }}" href="{{ url_for('admin.admin_users', local_remote='local') }}">{{ _('Users') }}</a></li>
|
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_users' }}" href="{{ url_for('admin.admin_users', local_remote='local') }}">{{ _('Users') }}</a></li>
|
||||||
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_users_trash' }}" href="{{ url_for('admin.admin_users_trash', local_remote='local') }}">{{ _('Monitoring - users') }}</a></li>
|
|
||||||
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_trash' }}" href="{{ url_for('admin.admin_content_trash') }}">{{ _('Monitoring - content') }}</a></li>
|
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_trash' }}" href="{{ url_for('admin.admin_content_trash') }}">{{ _('Monitoring - content') }}</a></li>
|
||||||
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_spam' }}" href="{{ url_for('admin.admin_content_spam') }}">{{ _('Monitoring - spammy content') }}</a></li>
|
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_spam' }}" href="{{ url_for('admin.admin_content_spam') }}">{{ _('Monitoring - spammy content') }}</a></li>
|
||||||
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_deleted' }}" href="{{ url_for('admin.admin_content_deleted') }}">{{ _('Deleted content') }}</a></li>
|
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_deleted' }}" href="{{ url_for('admin.admin_content_deleted') }}">{{ _('Deleted content') }}</a></li>
|
||||||
|
|
68
app/templates/user/edit_note.html
Normal file
68
app/templates/user/edit_note.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') -%}
|
||||||
|
{% extends 'themes/' + theme() + '/base.html' %}
|
||||||
|
{% else -%}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% endif -%}
|
||||||
|
{% from 'bootstrap/form.html' import render_form %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col col-login mx-auto">
|
||||||
|
<div class="card mt-5">
|
||||||
|
<div class="card-body p-6">
|
||||||
|
<div class="card-title">{{ _('Edit note for "%(user_name)s"', user_name=user.display_name()) }}</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<strong>{{ _('Emoji quick access') }}</strong>
|
||||||
|
<div>
|
||||||
|
<button id="thumbsup" class="emojitoggle">👍</button>
|
||||||
|
<button id="thumbsdown" class="emojitoggle">👎</button>
|
||||||
|
<button id="smile" class="emojitoggle">😄</button>
|
||||||
|
<button id="party-popper" class="emojitoggle">🎉</button>
|
||||||
|
<button id="frown" class="emojitoggle">😕</button>
|
||||||
|
<button id="red-heart" class="emojitoggle">❤️</button>
|
||||||
|
<button id="rocket" class="emojitoggle">🚀</button>
|
||||||
|
<button id="eyes" class="emojitoggle">👀</button>
|
||||||
|
</div><div>
|
||||||
|
<button id="star" class="emojitoggle">⭐</button>
|
||||||
|
<button id="medal" class="emojitoggle">🥇</button>
|
||||||
|
<button id="check" class="emojitoggle">☑️</button>
|
||||||
|
<button id="fire" class="emojitoggle">🔥</button>
|
||||||
|
<button id="robot" class="emojitoggle">🤖</button>
|
||||||
|
<button id="ghost" class="emojitoggle">👻</button>
|
||||||
|
<button id="clown" class="emojitoggle">🤡</button>
|
||||||
|
<button id="poo" class="emojitoggle">💩</button>
|
||||||
|
</div><div>
|
||||||
|
<button id="speech-bubble" class="emojitoggle">💬</button>
|
||||||
|
<button id="anger-bubble" class="emojitoggle">🗯️</button>
|
||||||
|
<button id="hundred" class="emojitoggle">💯</button>
|
||||||
|
<button id="rofl" class="emojitoggle">🤣</button>
|
||||||
|
<button id="zany" class="emojitoggle">🤪</button>
|
||||||
|
<button id="warning" class="emojitoggle">⚠️</button>
|
||||||
|
<button id="no-entry" class="emojitoggle">⛔</button>
|
||||||
|
<button id="vomit" class="emojitoggle">🤮</button>
|
||||||
|
</div>
|
||||||
|
{{ render_form(form) }}
|
||||||
|
<div class="row mt-5"><small class="field_hint">{{ _('This note appears next to their username. It\'s meant just for you and not displayed to anyone else.') }}</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script nonce="{{ session['nonce'] }}" type="text/javascript">
|
||||||
|
function addtext(text) {
|
||||||
|
var note = document.getElementById("note");
|
||||||
|
newtext = note.value.replaceAll(text, "");
|
||||||
|
if (newtext == note.value) {
|
||||||
|
note.value += text;
|
||||||
|
} else {
|
||||||
|
note.value = newtext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
document.querySelectorAll("button.emojitoggle").forEach(function(button) {
|
||||||
|
var emoji = button.textContent || button.innerText;
|
||||||
|
button.addEventListener('click', function() {addtext(emoji);});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -100,6 +100,7 @@
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
<li><a class="dropdown-item" href="{{ url_for('user.report_profile', actor=user.link()) }}" rel="nofollow">{{ _('Report') }}</a></li>
|
<li><a class="dropdown-item" href="{{ url_for('user.report_profile', actor=user.link()) }}" rel="nofollow">{{ _('Report') }}</a></li>
|
||||||
|
<li><a class="dropdown-item" href="{{ url_for('user.edit_user_note', actor=user.link()) }}" rel="nofollow">{{ _('Edit note') }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -108,6 +109,8 @@
|
||||||
{% if user.is_instance_admin() or (user.is_local() and user.is_admin()) %}<span class="red">({{ _('Admin') }})</span>{% endif %}<br />
|
{% if user.is_instance_admin() or (user.is_local() and user.is_admin()) %}<span class="red">({{ _('Admin') }})</span>{% endif %}<br />
|
||||||
{% if user.is_admin() or user.is_staff() %}{{ _('Role permissions') }}: {% if user.is_admin() %}{{ _('Admin') }}{% endif %} {% if user.is_staff() %}{{ _('Staff') }}{% endif %}<br />{% endif %}
|
{% if user.is_admin() or user.is_staff() %}{{ _('Role permissions') }}: {% if user.is_admin() %}{{ _('Admin') }}{% endif %} {% if user.is_staff() %}{{ _('Staff') }}{% endif %}<br />{% endif %}
|
||||||
{{ _('Joined') }}: {{ arrow.get(user.created).humanize(locale=locale) }}<br />
|
{{ _('Joined') }}: {{ arrow.get(user.created).humanize(locale=locale) }}<br />
|
||||||
|
{% if current_user.is_authenticated and current_user.is_admin() %}{{ _('Referer') }}: <span title="{{ _('Which website linked to PieFed when the user initially registered.') }}">{{ user.referrer if user.referrer }}</span><br />{% endif %}
|
||||||
|
{% if current_user.is_authenticated and current_user.is_admin() %}{{ _('IP and country code') }}: <span title="{{ _('IP address of last interaction.') }}">{{ user.ip_address if user.ip_address }}{% if user.ip_address_country %} ({{ user.ip_address_country }}){% endif %}</span><br />{% endif %}
|
||||||
{% if current_user.is_authenticated and current_user.is_admin() and user.last_seen %}{{ _('Active') }}: {{ arrow.get(user.last_seen).humanize(locale=locale) }}<br />{% endif %}
|
{% if current_user.is_authenticated and current_user.is_admin() and user.last_seen %}{{ _('Active') }}: {{ arrow.get(user.last_seen).humanize(locale=locale) }}<br />{% endif %}
|
||||||
{% if user.bot %}
|
{% if user.bot %}
|
||||||
{{ _('Bot Account') }}<br />
|
{{ _('Bot Account') }}<br />
|
||||||
|
@ -116,6 +119,7 @@
|
||||||
{% if current_user.is_authenticated and current_user.is_admin() and user.reputation %}{{ _('Reputation') }}: <span title="{{ _('Reputation: The Karma of the account. Total up votes minus down votes they got.') }}">{{ user.reputation | round | int }}</span><br />{% endif %}
|
{% if current_user.is_authenticated and current_user.is_admin() and user.reputation %}{{ _('Reputation') }}: <span title="{{ _('Reputation: The Karma of the account. Total up votes minus down votes they got.') }}">{{ user.reputation | round | int }}</span><br />{% endif %}
|
||||||
{{ _('Posts') }}: {{ user.post_count }}<br />
|
{{ _('Posts') }}: {{ user.post_count }}<br />
|
||||||
{{ _('Comments') }}: {{ user.post_reply_count }}<br />
|
{{ _('Comments') }}: {{ user.post_reply_count }}<br />
|
||||||
|
{% if current_user.is_authenticated %}{{ _('User note') }}: {{ user.get_note(current_user) }}<br />{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<div class="profile_bio">
|
<div class="profile_bio">
|
||||||
{{ user.about_html|safe }}
|
{{ user.about_html|safe }}
|
||||||
|
|
|
@ -147,3 +147,8 @@ class RemoteFollowForm(FlaskForm):
|
||||||
|
|
||||||
instance_type = SelectField(_l('Instance type'), choices=type_choices, render_kw={'class': 'form-select'})
|
instance_type = SelectField(_l('Instance type'), choices=type_choices, render_kw={'class': 'form-select'})
|
||||||
submit = SubmitField(_l('View profile on remote instance'))
|
submit = SubmitField(_l('View profile on remote instance'))
|
||||||
|
|
||||||
|
|
||||||
|
class UserNoteForm(FlaskForm):
|
||||||
|
note = StringField(_l('User note'), validators=[Optional(), Length(max=50)])
|
||||||
|
submit = SubmitField(_l('Save note'))
|
||||||
|
|
|
@ -16,10 +16,10 @@ from app.constants import *
|
||||||
from app.email import send_verification_email
|
from app.email import send_verification_email
|
||||||
from app.models import Post, Community, CommunityMember, User, PostReply, PostVote, Notification, utcnow, File, Site, \
|
from app.models import Post, Community, CommunityMember, User, PostReply, PostVote, Notification, utcnow, File, Site, \
|
||||||
Instance, Report, UserBlock, CommunityBan, CommunityJoinRequest, CommunityBlock, Filter, Domain, DomainBlock, \
|
Instance, Report, UserBlock, CommunityBan, CommunityJoinRequest, CommunityBlock, Filter, Domain, DomainBlock, \
|
||||||
InstanceBlock, NotificationSubscription, PostBookmark, PostReplyBookmark, read_posts, Topic
|
InstanceBlock, NotificationSubscription, PostBookmark, PostReplyBookmark, read_posts, Topic, UserNote
|
||||||
from app.user import bp
|
from app.user import bp
|
||||||
from app.user.forms import ProfileForm, SettingsForm, DeleteAccountForm, ReportUserForm, \
|
from app.user.forms import ProfileForm, SettingsForm, DeleteAccountForm, ReportUserForm, \
|
||||||
FilterForm, KeywordFilterEditForm, RemoteFollowForm, ImportExportForm
|
FilterForm, KeywordFilterEditForm, RemoteFollowForm, ImportExportForm, UserNoteForm
|
||||||
from app.user.utils import purge_user_then_delete, unsubscribe_from_community
|
from app.user.utils import purge_user_then_delete, unsubscribe_from_community
|
||||||
from app.utils import get_setting, render_template, markdown_to_html, user_access, markdown_to_text, shorten_string, \
|
from app.utils import get_setting, render_template, markdown_to_html, user_access, markdown_to_text, shorten_string, \
|
||||||
is_image_url, ensure_directory_exists, gibberish, file_get_contents, community_membership, user_filters_home, \
|
is_image_url, ensure_directory_exists, gibberish, file_get_contents, community_membership, user_filters_home, \
|
||||||
|
@ -1330,3 +1330,36 @@ def user_read_posts_delete():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(_('Reading history has been deleted'))
|
flash(_('Reading history has been deleted'))
|
||||||
return redirect(url_for('user.user_read_posts'))
|
return redirect(url_for('user.user_read_posts'))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/u/<actor>/note', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_user_note(actor):
|
||||||
|
actor = actor.strip()
|
||||||
|
if '@' in actor:
|
||||||
|
user: User = User.query.filter_by(ap_id=actor, deleted=False).first()
|
||||||
|
else:
|
||||||
|
user: User = User.query.filter_by(user_name=actor, deleted=False, ap_id=None).first()
|
||||||
|
if user is None:
|
||||||
|
abort(404)
|
||||||
|
form = UserNoteForm()
|
||||||
|
if form.validate_on_submit() and not current_user.banned:
|
||||||
|
text = form.note.data.strip()
|
||||||
|
usernote = UserNote.query.filter(UserNote.target_id == user.id, UserNote.user_id == current_user.id).first()
|
||||||
|
if usernote:
|
||||||
|
usernote.body = text
|
||||||
|
else:
|
||||||
|
usernote = UserNote(target_id=user.id, user_id=current_user.id, body=text)
|
||||||
|
db.session.add(usernote)
|
||||||
|
db.session.commit()
|
||||||
|
cache.delete_memoized(User.get_note, user, current_user)
|
||||||
|
|
||||||
|
flash(_('Your changes have been saved.'), 'success')
|
||||||
|
goto = request.args.get('redirect') if 'redirect' in request.args else f'/u/{actor}'
|
||||||
|
return redirect(goto)
|
||||||
|
|
||||||
|
elif request.method == 'GET':
|
||||||
|
form.note.data = user.get_note(current_user)
|
||||||
|
|
||||||
|
return render_template('user/edit_note.html', title=_('Edit note'), form=form, user=user,
|
||||||
|
menu_topics=menu_topics(), site=g.site)
|
||||||
|
|
Loading…
Add table
Reference in a new issue