mirror of
https://gitlab.com/SIGBUS/nyaa.git
synced 2024-12-22 19:19:59 +00:00
DB CHANGE: Add uploader ip address to torrent column and show on torrent view page for superadmins.
Added migration script!: remove sukebei_ lines if your local db does not have those. Show users ip address on user page for superadmins. Rename Admin to Moderator internally. Moderators can now change user level to trusted. Superadmins can make users moderator. Improve changing user level.
This commit is contained in:
parent
152e547ac5
commit
9af778217b
30
migrations/versions/3001f79b7722_add_torrents.uploader_ip.py
Normal file
30
migrations/versions/3001f79b7722_add_torrents.uploader_ip.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
"""Add uploader_ip column to torrents table.
|
||||||
|
|
||||||
|
Revision ID: 3001f79b7722
|
||||||
|
Revises:
|
||||||
|
Create Date: 2017-05-21 18:01:35.472717
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '3001f79b7722'
|
||||||
|
down_revision = None
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('nyaa_torrents', sa.Column('uploader_ip', sa.Binary(), nullable=True))
|
||||||
|
op.add_column('sukebei_torrents', sa.Column('uploader_ip', sa.Binary(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('nyaa_torrents', 'uploader_ip')
|
||||||
|
op.drop_column('sukebei_torrents', 'uploader_ip')
|
||||||
|
# ### end Alembic commands ###
|
|
@ -1,3 +1,4 @@
|
||||||
|
import flask
|
||||||
from nyaa import app, db
|
from nyaa import app, db
|
||||||
from nyaa import models, forms
|
from nyaa import models, forms
|
||||||
from nyaa import bencode, utils
|
from nyaa import bencode, utils
|
||||||
|
@ -8,6 +9,7 @@ import json
|
||||||
from werkzeug import secure_filename
|
from werkzeug import secure_filename
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from orderedset import OrderedSet
|
from orderedset import OrderedSet
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
|
|
||||||
def _replace_utf8_values(dict_or_list):
|
def _replace_utf8_values(dict_or_list):
|
||||||
|
@ -53,7 +55,8 @@ def handle_torrent_upload(upload_form, uploading_user=None, fromAPI=False):
|
||||||
description=description,
|
description=description,
|
||||||
encoding=torrent_encoding,
|
encoding=torrent_encoding,
|
||||||
filesize=torrent_filesize,
|
filesize=torrent_filesize,
|
||||||
user=uploading_user)
|
user=uploading_user,
|
||||||
|
uploader_ip=ip_address(flask.request.remote_addr).packed)
|
||||||
|
|
||||||
# Store bencoded info_dict
|
# Store bencoded info_dict
|
||||||
torrent.info = models.TorrentInfo(info_dict=torrent_data.bencoded_info_dict)
|
torrent.info = models.TorrentInfo(info_dict=torrent_data.bencoded_info_dict)
|
||||||
|
|
|
@ -263,7 +263,7 @@ class UploadForm(FlaskForm):
|
||||||
|
|
||||||
|
|
||||||
class UserForm(FlaskForm):
|
class UserForm(FlaskForm):
|
||||||
user_class = DisabledSelectField('Change User Class')
|
user_class = SelectField('Change User Class')
|
||||||
|
|
||||||
def validate_user_class(form, field):
|
def validate_user_class(form, field):
|
||||||
if not field.data:
|
if not field.data:
|
||||||
|
@ -294,7 +294,8 @@ def _validate_trackers(torrent_dict, tracker_to_check_for=None):
|
||||||
for announce in announce_list:
|
for announce in announce_list:
|
||||||
_validate_list(announce, 'announce-list item')
|
_validate_list(announce, 'announce-list item')
|
||||||
|
|
||||||
announce_string = _validate_bytes(announce[0], 'announce-list item url', test_decode='utf-8')
|
announce_string = _validate_bytes(
|
||||||
|
announce[0], 'announce-list item url', test_decode='utf-8')
|
||||||
if tracker_to_check_for and announce_string.lower() == tracker_to_check_for.lower():
|
if tracker_to_check_for and announce_string.lower() == tracker_to_check_for.lower():
|
||||||
tracker_found = True
|
tracker_found = True
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from sqlalchemy import func, ForeignKeyConstraint, Index
|
||||||
from sqlalchemy_utils import ChoiceType, EmailType, PasswordType
|
from sqlalchemy_utils import ChoiceType, EmailType, PasswordType
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from sqlalchemy_fulltext import FullText
|
from sqlalchemy_fulltext import FullText
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import base64
|
import base64
|
||||||
|
@ -61,6 +62,7 @@ class Torrent(db.Model):
|
||||||
encoding = db.Column(db.String(length=32), nullable=False)
|
encoding = db.Column(db.String(length=32), nullable=False)
|
||||||
flags = db.Column(db.Integer, default=0, nullable=False, index=True)
|
flags = db.Column(db.Integer, default=0, nullable=False, index=True)
|
||||||
uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||||
|
uploader_ip = db.Column(db.Binary(length=16), default=None, nullable=True)
|
||||||
has_torrent = db.Column(db.Boolean, nullable=False, default=False)
|
has_torrent = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
created_time = db.Column(db.DateTime(timezone=False), default=datetime.utcnow, nullable=False)
|
created_time = db.Column(db.DateTime(timezone=False), default=datetime.utcnow, nullable=False)
|
||||||
|
@ -138,6 +140,11 @@ class Torrent(db.Model):
|
||||||
def magnet_uri(self):
|
def magnet_uri(self):
|
||||||
return create_magnet(self)
|
return create_magnet(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uploader_ip_string(self):
|
||||||
|
if self.uploader_ip:
|
||||||
|
return str(ip_address(self.uploader_ip))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def anonymous(self):
|
def anonymous(self):
|
||||||
return self.flags & TorrentFlags.ANONYMOUS
|
return self.flags & TorrentFlags.ANONYMOUS
|
||||||
|
@ -313,7 +320,7 @@ class SubCategory(db.Model):
|
||||||
class UserLevelType(IntEnum):
|
class UserLevelType(IntEnum):
|
||||||
REGULAR = 0
|
REGULAR = 0
|
||||||
TRUSTED = 1
|
TRUSTED = 1
|
||||||
ADMIN = 2
|
MODERATOR = 2
|
||||||
SUPERADMIN = 3
|
SUPERADMIN = 3
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,6 +369,11 @@ class User(db.Model):
|
||||||
]
|
]
|
||||||
return all(checks)
|
return all(checks)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ip_string(self):
|
||||||
|
if self.last_login_ip:
|
||||||
|
return str(ip_address(self.last_login_ip))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def by_id(cls, id):
|
def by_id(cls, id):
|
||||||
return cls.query.get(id)
|
return cls.query.get(id)
|
||||||
|
@ -381,8 +393,8 @@ class User(db.Model):
|
||||||
return cls.by_username(username_or_email) or cls.by_email(username_or_email)
|
return cls.by_username(username_or_email) or cls.by_email(username_or_email)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_admin(self):
|
def is_moderator(self):
|
||||||
return self.level >= UserLevelType.ADMIN
|
return self.level >= UserLevelType.MODERATOR
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_superadmin(self):
|
def is_superadmin(self):
|
||||||
|
|
|
@ -11,7 +11,7 @@ import config
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import ipaddress
|
from ipaddress import ip_address
|
||||||
import os.path
|
import os.path
|
||||||
import base64
|
import base64
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
@ -135,6 +135,7 @@ def get_category_id_map():
|
||||||
|
|
||||||
app.register_blueprint(api_handler.api_blueprint, url_prefix='/api')
|
app.register_blueprint(api_handler.api_blueprint, url_prefix='/api')
|
||||||
|
|
||||||
|
|
||||||
def chain_get(source, *args):
|
def chain_get(source, *args):
|
||||||
''' Tries to return values from source by the given keys.
|
''' Tries to return values from source by the given keys.
|
||||||
Returns None if none match.
|
Returns None if none match.
|
||||||
|
@ -146,6 +147,7 @@ def chain_get(source, *args):
|
||||||
return value
|
return value
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@app.route('/rss', defaults={'rss': True})
|
@app.route('/rss', defaults={'rss': True})
|
||||||
@app.route('/', defaults={'rss': False})
|
@app.route('/', defaults={'rss': False})
|
||||||
def home(rss):
|
def home(rss):
|
||||||
|
@ -194,7 +196,7 @@ def home(rss):
|
||||||
|
|
||||||
if flask.g.user:
|
if flask.g.user:
|
||||||
query_args['logged_in_user'] = flask.g.user
|
query_args['logged_in_user'] = flask.g.user
|
||||||
if flask.g.user.is_admin: # God mode
|
if flask.g.user.is_moderator: # God mode
|
||||||
query_args['admin'] = True
|
query_args['admin'] = True
|
||||||
|
|
||||||
# If searching, we get results from elastic search
|
# If searching, we get results from elastic search
|
||||||
|
@ -215,7 +217,8 @@ def home(rss):
|
||||||
if render_as_rss:
|
if render_as_rss:
|
||||||
return render_rss('"{}"'.format(search_term), query_results, use_elastic=True, magnet_links=use_magnet_links)
|
return render_rss('"{}"'.format(search_term), query_results, use_elastic=True, magnet_links=use_magnet_links)
|
||||||
else:
|
else:
|
||||||
rss_query_string = _generate_query_string(search_term, category, quality_filter, user_name)
|
rss_query_string = _generate_query_string(
|
||||||
|
search_term, category, quality_filter, user_name)
|
||||||
max_results = min(max_search_results, query_results['hits']['total'])
|
max_results = min(max_search_results, query_results['hits']['total'])
|
||||||
# change p= argument to whatever you change page_parameter to or pagination breaks
|
# change p= argument to whatever you change page_parameter to or pagination breaks
|
||||||
pagination = Pagination(p=query_args['page'], per_page=results_per_page,
|
pagination = Pagination(p=query_args['page'], per_page=results_per_page,
|
||||||
|
@ -238,7 +241,8 @@ def home(rss):
|
||||||
if render_as_rss:
|
if render_as_rss:
|
||||||
return render_rss('Home', query, use_elastic=False, magnet_links=use_magnet_links)
|
return render_rss('Home', query, use_elastic=False, magnet_links=use_magnet_links)
|
||||||
else:
|
else:
|
||||||
rss_query_string = _generate_query_string(search_term, category, quality_filter, user_name)
|
rss_query_string = _generate_query_string(
|
||||||
|
search_term, category, quality_filter, user_name)
|
||||||
# Use elastic is always false here because we only hit this section
|
# Use elastic is always false here because we only hit this section
|
||||||
# if we're browsing without a search term (which means we default to DB)
|
# if we're browsing without a search term (which means we default to DB)
|
||||||
# or if ES is disabled
|
# or if ES is disabled
|
||||||
|
@ -256,22 +260,23 @@ def view_user(user_name):
|
||||||
if not user:
|
if not user:
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
if flask.g.user and flask.g.user.id != user.id:
|
admin_form = None
|
||||||
admin = flask.g.user.is_admin
|
if flask.g.user and flask.g.user.is_moderator and flask.g.user.level > user.level:
|
||||||
superadmin = flask.g.user.is_superadmin
|
admin_form = forms.UserForm()
|
||||||
else:
|
default, admin_form.user_class.choices = _create_user_class_choices(user)
|
||||||
admin = False
|
if flask.request.method == 'GET':
|
||||||
superadmin = False
|
admin_form.user_class.data = default
|
||||||
|
|
||||||
form = forms.UserForm()
|
if flask.request.method == 'POST' and admin_form and admin_form.validate():
|
||||||
form.user_class.choices = _create_user_class_choices()
|
selection = admin_form.user_class.data
|
||||||
if flask.request.method == 'POST' and form.validate():
|
|
||||||
selection = form.user_class.data
|
|
||||||
|
|
||||||
if selection == 'regular':
|
if selection == 'regular':
|
||||||
user.level = models.UserLevelType.REGULAR
|
user.level = models.UserLevelType.REGULAR
|
||||||
elif selection == 'trusted':
|
elif selection == 'trusted':
|
||||||
user.level = models.UserLevelType.TRUSTED
|
user.level = models.UserLevelType.TRUSTED
|
||||||
|
elif selection == 'moderator':
|
||||||
|
user.level = models.UserLevelType.MODERATOR
|
||||||
|
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -311,7 +316,7 @@ def view_user(user_name):
|
||||||
|
|
||||||
if flask.g.user:
|
if flask.g.user:
|
||||||
query_args['logged_in_user'] = flask.g.user
|
query_args['logged_in_user'] = flask.g.user
|
||||||
if flask.g.user.is_admin: # God mode
|
if flask.g.user.is_moderator: # God mode
|
||||||
query_args['admin'] = True
|
query_args['admin'] = True
|
||||||
|
|
||||||
# Use elastic search for term searching
|
# Use elastic search for term searching
|
||||||
|
@ -344,9 +349,7 @@ def view_user(user_name):
|
||||||
user_page=True,
|
user_page=True,
|
||||||
rss_filter=rss_query_string,
|
rss_filter=rss_query_string,
|
||||||
level=user_level,
|
level=user_level,
|
||||||
admin=admin,
|
admin_form=admin_form)
|
||||||
superadmin=superadmin,
|
|
||||||
form=form)
|
|
||||||
# Similar logic as home page
|
# Similar logic as home page
|
||||||
else:
|
else:
|
||||||
if use_elastic:
|
if use_elastic:
|
||||||
|
@ -362,9 +365,7 @@ def view_user(user_name):
|
||||||
user_page=True,
|
user_page=True,
|
||||||
rss_filter=rss_query_string,
|
rss_filter=rss_query_string,
|
||||||
level=user_level,
|
level=user_level,
|
||||||
admin=admin,
|
admin_form=admin_form)
|
||||||
superadmin=superadmin,
|
|
||||||
form=form)
|
|
||||||
|
|
||||||
|
|
||||||
@app.template_filter('rfc822')
|
@app.template_filter('rfc822')
|
||||||
|
@ -417,7 +418,7 @@ def login():
|
||||||
return flask.redirect(flask.url_for('login'))
|
return flask.redirect(flask.url_for('login'))
|
||||||
|
|
||||||
user.last_login_date = datetime.utcnow()
|
user.last_login_date = datetime.utcnow()
|
||||||
user.last_login_ip = ipaddress.ip_address(flask.request.remote_addr).packed
|
user.last_login_ip = ip_address(flask.request.remote_addr).packed
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -451,7 +452,7 @@ def register():
|
||||||
if flask.request.method == 'POST' and form.validate():
|
if flask.request.method == 'POST' and form.validate():
|
||||||
user = models.User(username=form.username.data.strip(),
|
user = models.User(username=form.username.data.strip(),
|
||||||
email=form.email.data.strip(), password=form.password.data)
|
email=form.email.data.strip(), password=form.password.data)
|
||||||
user.last_login_ip = ipaddress.ip_address(flask.request.remote_addr).packed
|
user.last_login_ip = ip_address(flask.request.remote_addr).packed
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -479,13 +480,7 @@ def profile():
|
||||||
|
|
||||||
form = forms.ProfileForm(flask.request.form)
|
form = forms.ProfileForm(flask.request.form)
|
||||||
|
|
||||||
level = 'Regular'
|
level = ['Regular', 'Trusted', 'Moderator', 'Administrator'][flask.g.user.level]
|
||||||
if flask.g.user.is_admin:
|
|
||||||
level = 'Moderator'
|
|
||||||
if flask.g.user.is_superadmin: # check this second because we can be admin AND superadmin
|
|
||||||
level = 'Administrator'
|
|
||||||
elif flask.g.user.is_trusted:
|
|
||||||
level = 'Trusted'
|
|
||||||
|
|
||||||
if flask.request.method == 'POST' and form.validate():
|
if flask.request.method == 'POST' and form.validate():
|
||||||
user = flask.g.user
|
user = flask.g.user
|
||||||
|
@ -586,11 +581,11 @@ def view_torrent(torrent_id):
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
# Only allow admins see deleted torrents
|
# Only allow admins see deleted torrents
|
||||||
if torrent.deleted and not (viewer and viewer.is_admin):
|
if torrent.deleted and not (viewer and viewer.is_moderator):
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
# Only allow owners and admins to edit torrents
|
# Only allow owners and admins to edit torrents
|
||||||
can_edit = viewer and (viewer is torrent.user or viewer.is_admin)
|
can_edit = viewer and (viewer is torrent.user or viewer.is_moderator)
|
||||||
|
|
||||||
files = None
|
files = None
|
||||||
if torrent.filelist:
|
if torrent.filelist:
|
||||||
|
@ -614,11 +609,11 @@ def edit_torrent(torrent_id):
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
# Only allow admins edit deleted torrents
|
# Only allow admins edit deleted torrents
|
||||||
if torrent.deleted and not (editor and editor.is_admin):
|
if torrent.deleted and not (editor and editor.is_moderator):
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
# Only allow torrent owners or admins edit torrents
|
# Only allow torrent owners or admins edit torrents
|
||||||
if not editor or not (editor is torrent.user or editor.is_admin):
|
if not editor or not (editor is torrent.user or editor.is_moderator):
|
||||||
flask.abort(403)
|
flask.abort(403)
|
||||||
|
|
||||||
if flask.request.method == 'POST' and form.validate():
|
if flask.request.method == 'POST' and form.validate():
|
||||||
|
@ -636,7 +631,7 @@ def edit_torrent(torrent_id):
|
||||||
|
|
||||||
if editor.is_trusted:
|
if editor.is_trusted:
|
||||||
torrent.trusted = form.is_trusted.data
|
torrent.trusted = form.is_trusted.data
|
||||||
if editor.is_admin:
|
if editor.is_moderator:
|
||||||
torrent.deleted = form.is_deleted.data
|
torrent.deleted = form.is_deleted.data
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -739,11 +734,22 @@ def send_verification_email(to_address, activ_link):
|
||||||
server.quit()
|
server.quit()
|
||||||
|
|
||||||
|
|
||||||
def _create_user_class_choices():
|
def _create_user_class_choices(user):
|
||||||
choices = [('regular', 'Regular')]
|
choices = [('regular', 'Regular')]
|
||||||
if flask.g.user and flask.g.user.is_superadmin:
|
default = 'regular'
|
||||||
|
if flask.g.user:
|
||||||
|
if flask.g.user.is_moderator:
|
||||||
choices.append(('trusted', 'Trusted'))
|
choices.append(('trusted', 'Trusted'))
|
||||||
return choices
|
if flask.g.user.is_superadmin:
|
||||||
|
choices.append(('moderator', 'Moderator'))
|
||||||
|
|
||||||
|
if user:
|
||||||
|
if user.is_moderator:
|
||||||
|
default = 'moderator'
|
||||||
|
elif user.is_trusted:
|
||||||
|
default = 'trusted'
|
||||||
|
|
||||||
|
return default, choices
|
||||||
|
|
||||||
|
|
||||||
# #################################### STATIC PAGES ####################################
|
# #################################### STATIC PAGES ####################################
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="control-label">Torrent flags</label>
|
<label class="control-label">Torrent flags</label>
|
||||||
<div>
|
<div>
|
||||||
{% if editor.is_admin %}
|
{% if editor.is_moderator %}
|
||||||
<label class="btn btn-primary">
|
<label class="btn btn-primary">
|
||||||
{{ form.is_deleted }}
|
{{ form.is_deleted }}
|
||||||
Deleted
|
Deleted
|
||||||
|
|
|
@ -3,32 +3,38 @@
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% from "_formhelpers.html" import render_menu_with_button %}
|
{% from "_formhelpers.html" import render_menu_with_button %}
|
||||||
|
|
||||||
{% if superadmin %}
|
{% if g.user and g.user.is_moderator %}
|
||||||
<h2>User Information</h2><br>
|
<h2>User Information</h2><br>
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
<dt>User ID:</dt>
|
<dt>User ID:</dt>
|
||||||
<dd>{{user.id}}</dd>
|
<dd>{{ user.id }}</dd>
|
||||||
<dt>Account created on:</dt>
|
<dt>Account created on:</dt>
|
||||||
<dd>{{user.created_time}}</dd>
|
<dd>{{ user.created_time }}</dd>
|
||||||
<dt>Email address:</dt>
|
<dt>Email address:</dt>
|
||||||
<dd>{{user.email}}</dd>
|
<dd>{{ user.email }}</dd>
|
||||||
<dt>User class:</dt>
|
<dt>User class:</dt>
|
||||||
<dd>{{level}}</dd><br>
|
<dd>{{ level }}</dd>
|
||||||
|
{%- if g.user.is_superadmin -%}
|
||||||
|
<dt>Last login IP:</dt>
|
||||||
|
<dd>{{ user.ip_string }}</dd><br>
|
||||||
|
{%- endif -%}
|
||||||
</dl>
|
</dl>
|
||||||
|
{% if admin_form %}
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
{{ form.csrf_token }}
|
{{ admin_form.csrf_token }}
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{{ render_menu_with_button(form.user_class)}}
|
{{ render_menu_with_button(admin_form.user_class) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<br>
|
<br>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
Browsing {{user.username}}'s torrents
|
Browsing {{ user.username }}'s torrents
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{% include "search_results.html" %}
|
{% include "search_results.html" %}
|
||||||
|
|
|
@ -28,7 +28,10 @@
|
||||||
{%- if not torrent.anonymous and torrent.user -%}
|
{%- if not torrent.anonymous and torrent.user -%}
|
||||||
<a href="{{ user_url }}">{{ torrent.user.username }}</a>
|
<a href="{{ user_url }}">{{ torrent.user.username }}</a>
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
Anonymous {% if torrent.user and (viewer == torrent.user or viewer.is_admin) %}(<a href="{{ user_url }}">{{ torrent.user.username }}</a>){% endif %}
|
Anonymous {% if torrent.user and (viewer == torrent.user or viewer.is_moderator) %}(<a href="{{ user_url }}">{{ torrent.user.username }}</a>){% endif %}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if viewer and viewer.is_superadmin and torrent.uploader_ip -%}
|
||||||
|
({{ torrent.uploader_ip_string }})
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue